Skip to main content

Integrate Your UI Class into Synth Look And Feel

Posted by xuanyun on January 26, 2009 at 1:17 AM PST

Several years ago, when we talk about designing look and feel, we know it is about implementing different UI classes for different components.

Today since we are using JRE5 or above, we can use Synth look and feel, which
allow us to customize the look and feel by writing an XML file. Synth look and
feel provides some XML elements to define the painters for rendering the swing
components, it even allow us the write our own painter class and integrate into
the look and feel (using the painter element in XML).

It is true that we can use painters to draw anything we need on the component,
but can we control the component's behavior as well? In the pass, we can implement
a new UI class for the component to provide new behavior, and specify the UI
class in this way:

UIManager.put("ComboBoxUI", "com.easynth.test.lnf.EaSynthComboBoxUI");

It sets the class com.easynth.test.lnf.EaSynthComboBoxUI
as JComboBox component's UI cass, But now we are using Synth look and feel,
can we still use the UI class?

Fortunately the answer is positive, by using the defaultsProperty
element, we can integrate our own UI class into Synth look and feel. Here is
an example:

 <object id="EaSynth.ComboBox.UI" class="java.lang.String">
     <string>com.easynth.test.lnf.EaSynthComboBoxUI</string>
</object>
<defaultsProperty key="ComboBoxUI" type="idref" value="EaSynth.ComboBox.UI"/>

It is a snippet of the XML file which customize the whole look and feel. We
use the defaultsProperty element to set the UI class name of
JComboBox component, thus our new UI class is integrated into Synth look and
feel.

Related Topics >>

Comments

Hi yilile, maybe you can find some place within the L&F to workaround this, but I don't recommend to do so. I think the L&F should not change the behavior of custom renderer, unless the user change it in his code (just like what you did). I cleanner solution may be extending the JComobBox class, put the getRenderer().setEnabled(isEnabled()) in the overwrited setEnabled() method.

Hi Xuanyun, we encountered an issue with combobox. When the combobox is set to a customized renderer, it would fail to honor the Synth look and feel more or less due to the interruption of Synth internal structure. One of the side effects is that the look of non-editable combobox then looks like the look of editable combobox (i.e. with white background field look in the main area( the renderer in combo becomes opaque)). Also the renderer for disabled combobox does not display disabled. By looking at SynthComboBoxRenderer inner class of SynthComboBoxUI class, I can workaround this by setting "setEnabled(combo.isEnabled())" in our customized renderer. Anyway this is just not ideal solution, do you know any better way to handle this issue? Thanks!

Hi yilile, the text layout is not so good in Synth L&F, I am not sure if I am talking about the same issue than yours. If some menu items have icons but others has not, the text of menu items will not be aligned (and it may cause the text to move closer to the arrow icon), our workaround is adding icons to all menu items, if there is no suitable icon for an item, add a transparent empty icon to it.

Hi Xuan Yun, Another question about submenu with right arrow icon: some submenu text appears overlapping with the arrow icon or very close together. Insets or Menu.textIconGap doesn't take effect on this, do you know any way to make sure the gap between right of the text and arrow icon? Thanks!

Thanks Xuan Yun! I did set Table.rendererUseUIBorder to true in Table, I'll continue to investigate...

Hi yilile, seems you want to implement a special border for the table cell when it is selected, I had not tried that before. Maybe you can try specifying Table.rendererUseUIBorder property to true within the table style, to see if it work.

Hi Xuan Yun, Very cool! The way you showed me works very well! I just hought overriding default xml font could be easier than this, anyway, lucky me, I got it for free :-) Thanks a lot!! I have another question: I was trying to display border for the selected table cell by defining propery of Table.focusSelectedCellHighlightBorder within name binding style for "Table.cellRenderer" . It works in my SwingSet2 test program but doesn't work in our real application for some reason. I tried debugging in DefaultTableCellRenderer class, and I found "DefaultLookup.getBorder(this, ui, "Table.focusSelectedCellHighlightBorder")" returns null fwhen running in our real app but non-null for SwingSet2 test, I also found that if a painter is defined in it with a method of "labelBorder", that method is never triggered from that style with name of Table.cellRenderer. I just wonder if you have any experience or insight on this issue. Much Thanks to you!!

Hi yilile, you can replace the xml content in java code and push the L&F to reload its content. In EaSynth L&F 1.05+, a method named setDefaultFont() is provided, which is doing exactly what you want, you can take a look.

Hi Xuan Yun, I wonder if you know a way to override the xml default font with specified font at the subclass level of SynthLookAndFeel. We uses one default font for all UI components defined in xml. The reason is that when we switch to large font(120dpi) through desktop display property dialog, the font appears small, i.e. it doesn't reflect the setting of large font. I think we need to do the conversion from logical font size to physical font size. Then I need to know how to override the xml default font through code. Thank you very much again!

Thanks again Xuan Yun!! Both ways works! I chose the first one since doing so doesn't require to change our client app, and we honor the component's background color if it differs from our default background color defined in xml.

Hi yilile, you can use the background color from user in Synth painter, since you can get the current painting component in the paint method, and you can also invoke its getBackground() method too...... Another approach is to use the client property, using the putClientProperty() and getClientProperty() can pass more information to the painter.

Hi Xuanyuan again, I encounter another integration issue, that is, in our client application, for example, we use setBackground() to a particular color for a particular panel in the constructor code, but we have different background color painted for general panel in our Synth painter code. Then the color set in app would not be honored, always be overwritten by Synth painter. I wonder if there is any easy way to honor the setting in client app instead of tweaking the synth side. Thanks!

Thanks yilile for sharing your experience. About the toggle button style, I think it should be easy, since it is almost the same with the regular button style. When I work with the L&F IDE, I just duplicate the regular button style and rename the new one to "ToggleButtonStyle" and bind to the "ToggleButton" Region, copy anything inside "PRESSED" state to the "SELECTED" state, and it works. I never use the clone attribute, since it make things confusing.

Hi Xuan Yun, Now I encountered another problem with ToggleButton on toolbar, for some reason, MOUSE_OVER state is not triggered, I tried direct xml specification within this state, or paintToggleButtonBorder method in my painter, both are ignored, i.e. when mouse is over the toggle button, nothing changes, neither the paintToggleButtonBorder method is called, but SELECTED state is OK. Any idea on this? Thanks!

Oops, the xml line did not get in because No html allowed. anyway, the line I removed in the default style is opaque with value being true.

Just want to share this to prevent others from suffering the same. Now toolbar works just like magic after I removed in the default style. Don't know the reason behind but it works!

Hi Xuan Yun, Thanks much for your clarification, with which in my mind, I tried again, and it works now as expected. The previous problem was that I used clone style of MenuItem for my submenu, which honors MOUSE_OVER state, but Menu seems to honor SELECTED instead, so if I specify SELECTED state in place of the MOUSE_OVER, it works. Thanks a lot again! For toolbar, there must be certain magic there I missed, well, hope I'll work it through soon just like a magic :-) Your expertise is of great value for the developers in this area. Best wishes to you!

Hi yilile, the name binding style should be able to completely overwrite the region binding style, can you make sure you have defined the MOUSE_OVER state for name binding style? If the state is not found, it will try getting it from default (region binding)style. I did not try that on menu and submenu, please tell me your result. About the toolbar......I don't know, maybe there are some magic......I have customized several toolbar styles for our clients, no big problem yet :-)

Thanks for your suggestion on submenu, I tried name binding, and it works basically, but default style for Menu defined using region binding interfere with this name binding style in certain way, for instance, for MOUSE_OVER state, it uses the style defined for region binding style for Menu instead of that for the name binding style for my submenu. Is that name binding style supposed to overwrite the region binding style completely? For toolbar issue, there must be something else going on since I had tried every way you mentioned, and no use. Really strange! The toolbar always shows the parent panel background color. I do appreciated your great help!

Hello yilile, if you are using imagePainter, you may also need to set the attribute "stretch" to true, so that the whole background will be filled. If you are using painter instead, you can reference EaSynth Painter class in EaSynth L&F. For your menu customizing, you can use component name to bind a different style for submanu, I have another article about this: http://weblogs.java.net/blog/xuanyun/archive/2009/02/style_binding_i.html

I have another question: I have a Menu style specified in xml with white TEXT_FOREGROUND, but I want to have black text foreground for the submenu. Since submenu is a menu so submenu takes white text foreground but I do want black instead. How can I specify that in xml? There is also no associated painter method to do so. Thanks in advance!

Thanks Xuan Yun for your prompt response! In fact, I was using paintToolBarBackground() method to paint, and in the method, I also see the width and height passed in is the actual size of toolbar, but as a result, it only paints the left end of the small area where the handle icon locates, the rest area of toolbar is still the background color of the parent panel(even toobar's opaque is true), really frustrating! I was testing it with Java sample SwingSet2 application. I'm using jdk1.6.0_11release. Any thoughts on this? thanks again!

Hi yilile, the easiest way to customize the toolbar background is to use the ImagePainter, you can specify an image as the background of toolbar, by setting the source insets and destination insets, the background will be scaled as expected when the toolbar size is different than the image size. A more complete way is to write your own painter class and implement the paintToolBarBackground() method, you can paint anything as you need.

Hi Xuanyun, It was so great to find someone who is an expert on Synth. I've been struggling with setting toolbar background in Synth. No matter what I do, the background color only shows in the handle icon area at the very left end, the background color for toolbar always shows the background color of the panel which JToolBar is in or on even the opaque is true. Please help me! my email address: yle@spss.com

Hello chetan21pj, I have listed some links about Synth L&F development in earlier comment in this article, they are not sufficient but still useful. If you feel hard to fight with it, you may be interested in using a L&F IDE, you can take a look at http://www.easynth.com/products/EaSynthLookAndFeelDesigner.html

Xuanyun... can you please send the links/tutorial for SynthLnF (if you have), which would help in designing the UI faster

Hi Xuanyun.....i will go ahead and implement with the name approach

Hello chetan21pj, actually one name can be assigned to multiple components, so it will not be a problem, all components with same name will share the same style.

Hi Xuanyun, Thank you for the reply...your blog gives a nice way of using tricks for developers using SynthLnF . in case of Statusbar it would be fine set the name as the best UI design is to have a single Statusbar for a Frame. But if i want to create a new XYZUI for XYZ component. And this component is used morethan once in the same frame...this would result in some other issues. ?? Is there any other way of achieving the above ??

Hi chetan21pj , I guess your StatusBar class is extended from JPanel? since JPanel is used everywhere, you should not bind the statusbar style to the PANEL region, instead you should assign a name to the StatusBar component and bind the style to the name. You can read my another article about it http://weblogs.java.net/blog/xuanyun/archive/2009/02/style_binding_i.html

When i tried as above to set my own UI, i get exception mentioning "No ComponentUI" What i'm doing is 1=> Created a StatusBarUI (extends BasicPanelUI) 2=> Created a new component StatusBar, overridden the getUIClassID and returned "StatusBarUI" ??Any thoughts on what might be wrong??

When i tried as above to set my own UI, i get exception mentioning "No ComponentUI" What i'm doing is 1=> Created a StatusBarUI (extends BasicPanelUI) 2=> Created a new component StatusBar, overridden the getUIClassID and returned "StatusBarUI" ??Any thoughts on what might be wrong??

I agree, Synth lacks Documentation. Almost 2 Year ago i tried a bit to understand Synth. I found it buggy and unuseable. After such long period of silence someone figured out a bit, that's your articel is all about.

Hi josevnz, so far it is lack of materials of Synth, but I think the following links may be useful: (1) The official introduction of Synth: http://java.sun.com/docs/books/tutorial/uiswing/lookandfeel/synth.html (2) WIKI: http://en.wikipedia.org/wiki/Synth_Look_and_Feel (3) Synth XML File Format: http://java.sun.com/javase/6/docs/api/javax/swing/plaf/synth/doc-files/s... (4) Synth Component Specific Properties: http://java.sun.com/javase/6/docs/api/javax/swing/plaf/synth/doc-files/c...

Very interesting article, Do you know where is a full tutorial or books that cover synth in more depth? So far what I have found in Google is bits and pieces of information. Thanks!

Hi yilile, thanks for sharing this :-)

Hi Xuan Yun, Just want to share with you my solution to the problem with menu item text overlapping with the arrow button. I tried empty tranparent icon you suggested, and it works but with one drawback, e.g. it leaves one tiny dot at the position where the icon should appear, that is not so good. Anyway I found a good solution, that is, add following node to the menu item's style, which works idealy. "property key="MenuItem.margin" type="insets" value="0 0 0 16""

Hi laurah1220, the popup component is the container of the popupmenu object, they are different. The first part of that ariticle is not for popup component, but for the popupmenu instead, so it is not the part you need.

I am copying your first chunk of code from that page and it is not working. I am using Synth, not EaSynth, but this should work, right? I've tried everything - please help.

Hi laurah1220, I am not sure about that but I guess that 1 pixel may be from the background of the popup component. Maybe you can try doing something on the popup background, you can read my another article about this: http://weblogs.java.net/blog/xuanyun/archive/2009/03/implement_shado.html

HELP PLEASE! I have successfully changed the background image of the ScrollPane that is part of a JComboBox using Synth. This image has rounded corners. However, I can not get rid of the black 1px border surrounding the dropdown area. I believe this border is part of the JPopupMenu that is wrapped around the ScrollPane. I would think that I should just be able to use an imagePainter with the popupMenuBorder method, but it is not working. If anyone knows what to do, please help!

Thanks Xuanyun, the way you suggested does not work for this situation because the enable/disable state of the renderer in combo would be overwritten on the fly in customized renderer which usually uses setEnabled(list.getEnabled()), but list.getEnabled() always return true when we uses customized renderer. I'll keep my workaround described earlier before finding any better way to replace. And please let me know if you know any good way to do so in the future. Thanks again!