Thursday, January 28, 2010

Table and actions.

If you have been developing client software you certainly already ran into the following situation:
You have a table full of data, each line corresponding to an object you can interact with. Where and how should I put buttons for this purpose?

I will explore several implementations in order to handle this situation.

Cell editors/renderers


This way is often seen to handle this situation, you have to add one column per action, and then the button will appear on each line thanks to a cell renderer/editor.
I don’t find it really appealing visually, because repeating the same button over and over again on each line seems wrong to me, but at least it’s easy to see that the action on the button will be applied to the selected row.
Adding columns + most likely increasing the row height (to display properly an icon) are a waste of space if you don’t have so much of it at first.

I know by experience that developers that do not understand swing correctly (well, that’s a nice 99% of the people I worked with) tend to fail miserably at writing cell renderer/editor, is why I tried not to advice people to use custom cell editor/renderer.



Buttons outside the table

It’s possible to add buttons somewhere on the screen that will do something related to the selected row in the table, that’s the easiest solution. The main problem of this implementation is that buttons are actually outside the table and hence that the buttons are not yet directly linked to the selected row, it might confuse the user.



Popup menu

Right click on the table to show a popup menu
This one is also an easy solution.
The main drawback of this solution is that at first the user has no idea there is actions available on the selected row via a popup menu



Overlay buttons

So far the main drawbacks of the above solutions are: buttons not directly linked to the selected row, popup not visible at first, repeating the same button again and again.
So why not try to come with a solution that will meet the following requirement:
Only one button per action visible at the same time, linked to the selected row.
Let’s have a look at the outside button way. What was is main problem? The buttons are not linked to the selected row. So how would it be possible to link it?

The solution I came up with is to use a JLayeredPane to position the buttons next to the selected row: here




Mixed approach

What happen now if there are more than 2 actions, so 2+ buttons.
The cell renderer approach will need more even more column, so even more space wasted.
The button overlay approach will also need some space to show the button, and the furthest button will not seems so much connected to the selected row

Only the popup approach is behaving well in this case, but at the same time it’s not user friendly as the possibility to see the popup is not explicit.

(I am not talking about the buttons outside the table way because I consider it not user friendly, because of how unrelated are the buttons with the table)


So why not mixed the popup approach with the 2 others?

Popup + cell renderer/editor / Popup + overlay button
The principle is the same in both approach, that is, to show a button that say “click me to see the popup” and put it either as a cell renderer or as an overlay button.









You can find all the above implementations here

web start demo:


Thursday, January 21, 2010

Validation overlays

What I mean validation is for example to have an error icon in a corner of a text field if the input is wrong.


Like a year ago I read a serie of articles about validation overlays on Kirill Grouchnikov blog at http://www.pushing-pixels.org/?p=69 that I really encourage you to read to have some information/idea how this can be implemented. I came to the conclusion that there were only easy 2 ways of doing this, using glasspane or jxlayer.
The glasspane suffer a performance issue and by the fact that there is only 1 glasspane per window.
JXlayer only drawback is that you can’t put the icon outside of the component.

One day I noticed that jide provide an easy way to add an overlay component to any component.
There is one problem although with jide you have to override the paintComponent Method(and add the required code) of each component you want to have the overlay component.
That’s a constraint I really didn’t want to deal with to have validation overlays, that’s make the code too complicated/unreadable

If you don’t override the paint method, the error icon won’t get repainted after the overlaid component, so the result is that the error icon will be partially/totally hidden by the overlaid component.




So the idea was to have a way to repaint the error icon each time the overlaid component is painted without to have to override the paintcomponent method of each overlaid component.


I came up with the idea to wrap the jide overlayable component inside a JXLayer, and to call the paint component of the overlay component inside the paint layer of the jxlayer, and Voila! Now we have the better of the 2 worlds: the error icon can be outside out the overlaid component and the error icon will be repainted each time the overlaid component does.


The error icon appears/disappear with a smooth animation as I like it.
The icon is a true component so it can have a tooltip, in our case the tooltip will be the error message.
You can change the default error icon for all or for one component:
ValidationPanelIcon.setDefaultIcon(BufferedImage)
ValidationOverlay.setIcon(BufferedImage newicon)


You can change the default location of the icon:
By changing the values of

ValidationPanelIcon.DEFAULT_XOFFSET
ValidationPanelIcon.DEFAULT_YOFFSET
ValidationPanelIcon.DEFAULT_ICON_LOCATION


Or by calling
ValidationIOverlay.setIconLocation(int xoffset, int yoffset, int location)



As there is a JXlayer in use, why not also us it fully and paint something on the layer?

You can specify a ValidationPaint :
ValidationIOverlay.setPaint(ValidationPaint)


ValidationPaint is an abstact class with the following method:


/**
*
* @param g
* @param msg the list of error message
* @param width width of the layer
* @param height height of the layer
* @param animProgress goes from 0 to 1 and then from 1 to 0, the duration of the animation can be set using ValidationOverlay.setAnimationDuration
*/
public abstract void paint(Graphics2D g, List<String> msg, JXLayer<JComponent> layer, float animProgress);



A default implementation is available :
ValidationPaint.getColorValidationPaint(Color)

This default implementation paint a rectangle over the component of the given color with a blink animation

As an example with a custom ValidationPaint it’s possible to write the error messages directly on the layer





ValidationOverlayFactory

To create the validation component use ValidationOverlayFactory:

createBlinking(T, List<Validator<T>>)
createBlinking(T, Validator<T>)
createBlinkingAndIcon(T, Validator<T>)
createBlinkingAndIconComponent(T, List<Validator<T>>)
createErrorOverlayedIcon(T, List<Validator<T>>)
createErrorOverlayedIcon(T, Validator<T>)


Validator is a simple interface

public interface Validator<T extends JComponent> {
String validate(T comp);
}


The validate method should return the error message or null if no error has been detected.

To execute the validator you have to call
ValidationOverlayFactory.validate(JComponent)

The jcomponent given to this method is your original component on wich you add the overlay


TextValidationOverlayFactory is a facility class to create validation overlaid component for text component, it installs automatically a listener to call ValidationOverlayFactory.check on a text changed.





validation overlays source code here

Saturday, January 16, 2010

Animated cell renderer

This article will take as example list cell renderer but the same way can be applied to animate tree/table renderer.

Let’s start from where the last article end: the renderer is bigger when the cell is selected, why not animate the fact that the renderer size increase over time, to let the user know it’s actually the one that he selected that get bigger.

First we need a way to store the animation value for each cell:


Map<Integer, Float> mapAnimation = new HashMap<Integer, Float>();


The renderer will look up for the animation value:

public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, final boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index,isSelected, cellHasFocus);

Float progress = mapAnimation.get(index);
if (progress == null) {
progress = 0f;
}
label.setFont(label.getFont().deriveFont(10 + 20 * progress));
label.setPreferredSize(new Dimension(50, (int) (15 + 35 * progress)));
return label;
}




A selection listener to start the animation:
list.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
if (e.getValueIsAdjusting()) {
return;
}
final Timeline timeline = new Timeline();
timeline.setDuration(200);
timeline.addCallback(new TimelineCallback() {
/*…see below…*/
});
timeline.play();
}
});



The TimelineCallBack allow us to define what to do on each timeline pulse, it’s where the animation map will get populated.

new TimelineCallback() {
@Override
public void onTimelinePulse(float durationFraction,float TimelinePosition) {
// set the progress for the selected index
mapAnimation.put(list.getSelectedIndex(), durationFraction);
//set the progress for the last selected index
if (oldSelected[0] != -1) {
mapAnimation.put(oldSelected[0], 1 - durationFraction);
}
//compute the size for each cell with the new animation values
SwingUtilities.invokeLater(new Runnable(){
Override
public void run(){
JlistUtils.computeListSize(list);
}
});
}




And that’s it! You now have an animated renderer.




A bit more complex examples:













as always you can find the source code in the source code repository here

Dynamic size cell list renderer

What I mean by dynamic size is that the renderer can changed size for a given index.

Let’s take what can be seen at first as a very easy example:
For example when you select an item, you would like to increase the font size of the label.

It seems easy, let’s just create a custom renderer and change the font size based on the isSelected parameter.


public Component getListCellRendererComponent(JList list, Object value,int index, boolean isSelected, boolean cellHasFocus) {
JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (isSelected) {
label.setFont(label.getFont().deriveFont(35f));
} else {
label.setFont(label.getFont().deriveFont(10f));
}
return label;
}




The font size is bigger but the size of the label is not increased, hence the label is partly hidden .

So let’s try to change the size of the label:


public Component getListCellRendererComponent(JList list, Object value,int index, boolean isSelected, boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (isSelected) {
label.setFont(label.getFont().deriveFont(35f));
label.setPreferredSize(new Dimension(50, 50));
} else {
label.setFont(label.getFont().deriveFont(10f));
label.setPreferredSize(new Dimension(50, 15));
}
return label;
}



To our surprise it doesn’t work. Why?

The answer is in the BasicListUI class, the height of each cell is cached, it doesn’t get computed each time the getListCellRendererComponent method get called. That’s where our problem lies.

By looking a bit more closely at BasicListUI, one can see that the cache is populated in the updateLayoutState method and that’s only getting called after a change in the model.

We have to call this method in order to compute the new size of each cell, but this method is protected,so we won’t be able to call this method directly so we will use reflection.


public static void computeListSize(final JList list) {
if (list.getUI() instanceof BasicListUI) {
BasicListUI ui = (BasicListUI) list.getUI();

try {
Method method = BasicListUI.class.getDeclaredMethod("updateLayoutState");
method.setAccessible(true);
method.invoke(ui);
list.revalidate();
list.repaint();
} catch (Exception e) {
e.printStackTrace();
}
}
}




In our case we need to compute the cell size when a cell gets selected:

list.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(final ListSelectionEvent e) {
JlistUtils.computeListSize(list);
}
});



But as you can see if you run this webstart demo , it doesn’t work, so why ?
The answer is in the updateLayoutState method.:
protected void updateLayoutState(){
//…
Component c = renderer.getListCellRendererComponent(list, value, index, false, false);
//…
}



The 2 last parameters are isSelected and cellHasFocus, which means we can’t use isSelected+cellHasFocus to determine the size of the renderer.

So the last version of our renderer is:


public Component getListCellRendererComponent(JList list,Object value, int index, boolean isSelected, boolean cellHasFocus) {

JLabel label = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
if (list.getSelectedIndex() == index) {
label.setFont(label.getFont().deriveFont(35f));
label.setPreferredSize(new Dimension(50, 50));
} else{
label.setFont(label.getFont().deriveFont(10f));
label.setPreferredSize(new Dimension(50, 15));
}
return label;
}



This time we actually have what we expected: the cell selected is bigger.






You can find the source of those demo in the following package in the source code repository here


all the jars files has been signed to run via webstart; that was needed because of the use of Method.setAccessible to be able to call a protected method

Thursday, January 14, 2010

DialogWithDropShadow +JXLayer use case

The drop shadow dialog/frame from the last article is indeed not something to use everywhere.
There is one situation although I like to use it:



While opening a dialog over a JFrame, a gray filter is applied over the JFrame using JXLayer, the gray filter is of course faded in using a fast animation (500ms => the same time it take for the dialogWithDropShadow to fade in). I really like the result, it makes the dialog really stand out, and make the user focus on it.
Launch the web start demo to see the animation.

The fade in of the gray filter is done by animating the alpha property in the ColorLayerUI class.
This ColorLayerUI is really simple; it’s just paint a grey rectangle in the paintLayer method.


Saturday, January 9, 2010

Customs JDialog/JFrame

I wanted a JDialog/JFrame that looks better than the standards ones, so I ended up coding these classes : DialogWithDropShadow/FrameWithDropShadow , and yes something I am really bad at is naming.

They are JDialog / JFrame with round corner and drop shadow.

It’s JDialog/JFrame which has been set non opaque using the WindowsUtils class( wrapper around AWTUtilities + some other methods). It’s the custom content pane that deal with painting the background+shadow, and the layered pane is used to position the title bar


Under SubstanceDustCoffeeLookAndFeel



under metal LAF



Title bar

For the dialog you can choose to have (default value) or not the close button using the following constructor
DialogWithDropShadow (Window frame, boolean draggable, boolean withCloseButton)
or the setter
setWithCloseButton(boolean)
If the close button is not visible and that the title is empty there will be not y offset.










Background

You can change the background color using either getContentPane().setBackground(Color)









or setContentPaneBackground(Paint)












Fade in / Fade out

This window can be shown or hidden using a fade in/fade out animation with the 2 followings methods:

startShowAnim()

startHideAnim()

Those 2 methods in fact only call WindowFadeInManager.fadeIn/fadeout

WindowFadeInManager is a utility class used to fade in or fade out windows, you can use it on any window, but you will need at least java 6 update 10 to use this feature, if you don’t, it won’t crash, it will just setVisible true/false on the given window


Resizable

By default they are resizable, it uses com.jidesoft.swing.Resizable behind the scene to make it resizable, just call setResizable(false) to remove this default behavior.


Draggable
Both dialog and frame are draggable by default, not only on the title bar, from anywhere if the mouse event is not catched by another component. draggable class


You can find the source code here



dialog under substance

frame under substance

dialog under metal

frame under metal


Friday, January 8, 2010

Text prompt

I really like the solution to create a text prompt I saw at http://tips4java.wordpress.com/2009/11/29/text-prompt/

Something I really like also is smooth transition between component states, so I had to add fade in / fade out transition for this text prompt component!


My first idea was to do the animation using the alpha component of the foreground. But after the first test i noticed that i forgot the icon of the JLabel!

So i ended up using JXPanel on which one i add the JLabel, and then doing the animation of the JXPanel alpha property.


You can find the source in the repository here

Feel free to try it out:


I have one problem with this component, it only hapens under substance LAF, i have to create the textPrompt component in another invokeLater else i can't see the text prompt, i still need to investigate this point.


Ok, that's it for my first article on a swing component, i hope i didn't forget anything and that the web start demo works.

Libraries dependencies + source code repository

You can find the required libraries at:
http://kenai.com/projects/mabs-swing/downloads/download/lib.zip


You can find the source code repository at:
https://svn.kenai.com/svn/mabs-swing~source-code-repository


The source code contains already several components/demos. I am trying to centralise there all the common components I am usually using in my projects, I will try to write articles about most of them.

The main required libraries, I am using, are the following:
-Substance: My favourite LAF .
-Trident: for animations + required by substance
-Miglayout: the easiest and most readable layout manager around imho.
-SwingX
-JXlayer
-Filers from jhlabs
-jide-oss

Some components are developed to match specialy substance LAF by querying the skin to get colors, but even if I do that, I try to always see first if substance is the current LAF, if not I use some default colors.

Monday, January 4, 2010

Welcome to my blog

Hi and welcome to my blog.
That's been a few now that i am following some blogs about java swing/java2d.
I have been thinking to create this blog for a few now; with a new year it's a good time to start, so here we go.

So here will be the place where i will share interresting components or cool effects developed in swing/java2d