I have finish coding this since 3 weeks and still haven't found the time to write a post, so here we go : I have updated my "sexy" swing app with a component which is a mix of a JTable and a windows folder :
The size of the cells is dynamic, so for example if you move the slider at the bottom the content will grow or shrink :
It look best when you play with the zoom in real time, so download it and have a look! (see the links at the end of the post)
I was searching for a way to implement the selection by drag and drop with the mouse, and JXLayer come to the rescue. The API is very simple, but yet very powerful.
With help of JXLayer you can easily decorate your compound components and catch all Mouse, Keyboard and FocusEvent for all its subcomponents.Basically, you wrap your component into a layer which allow you to draw above or under the component. The "getting started" example will learn you all you need to know to start using JXLayer (it's really very simple and quick to learn).
So I used a ListSelectionModel for the logic and JXLayer for the drawing and events management. The result look like this :
I have used one layer on the whole grid component, to draw the selection rectangle when the user drag and drop to select cell.
JXLayer keep it simple!!
I can see plenty of different usage for this library, like a lightbox or a heat map over the UI.
About the JGrid component :
- it's working like a JList (both display a list of values ...)
- you can set renderer to customize each cell
- the code need to be improved a little :) but you can still get it from here (in the ilist.ui.generic.grid package). There still some little problem with selection too.
- it's scrollable, so you can embed it in a JScrollPane
JList grid = new JList(model); grid.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
// layout as a grid :
grid.setLayoutOrientation(JList.HORIZONTAL_WRAP);
grid.setVisibleRowCount(-1);
// cell size :
grid.setFixedCellHeight(100);
grid.setFixedCellWidth(100);
grid.setCellRenderer(myRenderer);
And if I wrap it with a JXLayer, i can have the "drag and drop" effect :
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import javax.swing.JList;
import javax.swing.SwingUtilities;
import org.jdesktop.jxlayer.JXLayer;
import org.jdesktop.jxlayer.plaf.AbstractLayerUI;
public class GridLayerUI extends AbstractLayerUI {
private Point startDrag, endDrag;
@Override
protected void processMouseEvent(MouseEvent e, JXLayer l) {
super.processMouseEvent(e, l);
if (e.getID() == MouseEvent.MOUSE_PRESSED)
startDrag = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
else if (e.getID() == MouseEvent.MOUSE_RELEASED) {
endDrag = null;
startDrag = null;
setDirty(true);
}
}
@Override
protected void processMouseMotionEvent(MouseEvent e, JXLayer l) {
super.processMouseMotionEvent(e, l);
if (e.getID() == MouseEvent.MOUSE_DRAGGED) {
endDrag = SwingUtilities.convertPoint(e.getComponent(), e.getPoint(), l);
if (startDrag != null) {
l.getView().getSelectionModel().clearSelection();
Rectangle dragZone = new Rectangle(Math.min(startDrag.x, endDrag.x), Math.min(startDrag.y, endDrag.y), Math.abs(endDrag.x - startDrag.x), Math.abs(endDrag.y - startDrag.y));
for (int i = 0; i < l.getView().getModel().getSize(); i++) {
if (l.getView().getCellBounds(i, i).intersects(dragZone))
l.getView().getSelectionModel().addSelectionInterval(i, i);
}
}
setDirty(true);
}
}
@Override
protected void paintLayer(Graphics2D g2, JXLayer l) {
super.paintLayer(g2, l);
if (endDrag != null && startDrag != null) {
// draw the selection rectangle when user drag & drop on the grid :
g2.setPaint(new Color(137, 177, 237, 128));
g2.fillRoundRect(Math.min(startDrag.x, endDrag.x), Math.min(startDrag.y, endDrag.y), Math.abs(endDrag.x - startDrag.x), Math.abs(endDrag.y - startDrag.y), 2, 2);
g2.setColor(new Color(19, 106, 197));
g2.drawRoundRect(Math.min(startDrag.x, endDrag.x), Math.min(startDrag.y, endDrag.y), Math.abs(endDrag.x - startDrag.x), Math.abs(endDrag.y - startDrag.y), 2, 2);
}
}
}
JXLayer gridLayer = new JXLayer(myList);
gridLayer.setUI(new GridLayerUI());
JScrollPane scrollPane = new JScrollPane(gridLayer);
The only problem is that by default JList select a cell in the list when you drag the mouse ... so you loose the selection done inside the layer :( The only solution I found is :
grid.putClientProperty("List.isFileList", Boolean.TRUE); // shame on me
which remove this feature. Yes it's a hack, and I'm currently searching for a best way to do this (may be by filtering the events with JXLayer ?).
About the "fake" application (see post #1 and post #2) :
- the breadcrumb/search field/menu list/back and previous buttons work *cough*almost*cough*
- the "new" button open a translucent panel, and also if you double click on a cell
- there is a little bit of Vista, Mac OS and others things ... have to work on homogeneity :)
- you can donwload it here (java -classpath ilist.jar;examples.jar;Filters.jar;jna.jar;plug-engine.jar;jxlayer.jar ilist.MainPlugin to start it, yes the jar is not runnable :( )
- download sources here




13 comments:
Hello; love your blog.
Can you explain how your JGrid component is different from a JList set to horizontal style with a custom renderer?
hello Laird,
you are right, it look like setLayoutOrientation with HORIZONTAL_WRAP ( http://java.sun.com/javase/6/docs/api/javax/swing/JList.html#setLayoutOrientation(int) ) do the same thing ... I didn't know it was possible :(
The only missing feature is the ability to select cell with drag and drop like in a folder. It should be possible to do this by wrapping a JList in a JXLayer ...
Thanks. (I too love JXLayer; it really is a tremendously useful tool.)
Please keep up the good work and good writing.
Cheers,
Laird
thx for your support :)
btw, I'm currently trying to rewrite it with a JList wraped in a JXLayer.
It's done !
I have updated the post/app/sources.
That was some fantastic swingy work. Bravo.
I ran against a little glitch. I am on WinXP with Java 6. But the borders are not coming rounded. A little white is visible at the corners of the app. Do you know, why that may happen?
Hello Ravi,
you're right, I have forgot to activate the translucency on the main frame.
(in the constructor of MainFrame, "WindowUtils.setWindowTransparent(this, true);" is in comment ...)
Here's an option for decorating drop targets on a given Swing component:
http://rabbit-hole.blogspot.com/2006/04/decoratingoverpainting-swing.html
It doesn't require any changes in your component hierarchy.
Source is available at furbelow.sf.net.
Is this source available via a BSD license? (I will be happy to give credit to Christophe in the About view)? If so, could you update the source file with the BSD license (available at http://en.wikipedia.org/wiki/BSD_license)
hello,
I have updated the sources with a BSD licence (http://www.opensource.org/licenses/bsd-license.php ).
You can download it at http://sites.google.com/site/christophe/files-1/iList-sources.rar?attredirects=0
Thanks! I'll post a link to the GUI when it's done (about 1.5 months) :). Your design looks great!
Ok - 1.5 months took a bit longer :) Take a look at your contribution to http://www.rockyourphone.com (Independent iPhone App Store). Download the desktop client and enjoy :). Great work.
Good job Mario !
Congratulation for you application !
Post a Comment