The Source for Java Technology Collaboration
User: Password:



Alexander Potochkin's Blog

June 2008 Archives


JXLayer 3.0 - MouseScrollableUI

Posted by alexfromsun on June 24, 2008 at 10:39 AM | Permalink | Comments (3)

The mouse auto-scrolling is very popular these days for modern applications. I usually use this feature in the Firefox browser - you click the mouse wheel somewhere on the page and can immediately start scrolling.

A few months ago Kirill wrote an great review of the "auto-scrolling feature" implementation in Swing. I'd like to add one more item to the list.

Suppose you create a JTable, wrap it with JScrollPane and add it to a frame:

JScrollPane sp = JScrollPane(createMyTable());
add(sp);             

To enable auto-scrolling you just need to wrap a JScrollPane with JXLayer and set the MouseScrollableUI to it:

JScrollPane sp = JScrollPane(createMyTable());
JXLayer<JScrollPane> l = new JXLayer<JScrollPane>(sp, new MouseScrollableUI());        
add(l);             

After that you can you can activate auto-scrolling by clicking the mouse wheel:

demo screenshot

When the scrolling indicator appears, your can scroll by moving your mouse towards the desirable direction. Auto-scrolling is deactivated by clicking any keyboard or mouse button.

Implementation

Things like MouseScrollableUI are very easily implemented with JXLayer, because it provides a way to catch all mouseEvents for all components inside a JXLayer.
class MyLayerUI extends AbstractLayerUI<JComponent> {
    
    // This method catches all focus, mouse and keyboard events
    // for the layer and all its subcomponents
    @Override
    public void eventDispatched(AWTEvent e, JXLayer<JComponent> l) {
        super.eventDispatched(e, l);
        System.out.println("AWTEvent is dispatched: " + e);
    }

    // Utility methods which are called from eventDispatched()
    // to provide a hook for catching particular type of events
    @Override
    protected void processMouseEvent(MouseEvent e, JXLayer<JComponent> l) {
        System.out.println("MouseEvent on component: " + e.getComponent());
    }
    
    @Override
    protected void processMouseMotionEvent(MouseEvent e, JXLayer<JComponent> l) {
        System.out.println("MouseMotionEvent on component: " + e.getComponent());
    }    
}

Once you have a way to catch all mouseEvents for all subcomponents, everything else is straightforward. The only specific details is the scrolling indicator, initially I thought to paint it, as usual, in the paintLayer() method.

However I realized that I want to change the mouse cursor when I move the mouse over scrolling indicator and the best solution is to implement it as a custom component, set a resize mouse cursor and place it at the layer's glassPane.

The source code of the MouseScrollableUI is quite compact, the MouseScrollableDemo for the webstart demo is even shorter.
(the runnable link is above the screenshot)

See you on the JXLayer forum

Thanks
alexp

JXLayer 3.0 - Getting started

Posted by alexfromsun on June 05, 2008 at 09:49 AM | Permalink | Comments (13)

It is my pleasure to announce a major update of JXLayer component. The new version is hosted on its own java.net project jxlayer.dev.java.net, where I will also provide links to all my blogs about this component.

So, why I encourage everybody to try out the new JXLayer?
Actually to answer this question I am going to write several more blogs, here is a short answer:

  1. more consistent and efficient API
  2. easier to use
  3. new functionality

The JXLayer 3.0 API is a bit different from the previous version and I had good reasons to make it this way. The good news is that I am very happy with the current API and I am not gonna introduce incompatible changes in the future. So, if you are using the previous JXLayer, it won't take much time to adjust your code for the new one, if you haven't tried it yet, it's high time to have a look.

JXLayer is the universal decorator for Swing components, it means that you have a flexible way to enrich the visual appearance of your components and control the input events for the whole component's hierarchy.

Just wrap your component with JXLayer and set a LayerUI which implements the required functionality. On jxlayer.dev.java.net you'll find demos of several carefully tested and ready to use LayerUI's implementations. In this entry we are going to see how to create your own ones.

Here is a simple example how to create and use LayerUI which paints translucent foreground over the layer:

        // wrap your component
        JXLayer<JComponent> layer = new JXLayer<JComponent>(myComponent);

        // create custom LayerUI
        AbstractLayerUI<JComponent> layerUI = new AbstractLayerUI<JComponent>() {

            @Override  
            protected void paintLayer(Graphics2D g2, JXLayer<JComponent> l) {
                // this paints layer as is
                super.paintLayer(g2, l);
                // custom painting:
                // here we paint translucent foreground
                // over the whole layer
                g2.setColor(new Color(0, 128, 0, 128));
                g2.fillRect(0, 0, l.getWidth(), l.getHeight());
            }
        };

        // set our LayerUI
        layer.setUI(layerUI);

        // add the layer as a usual component
        frame.add(layer);

BufferedLayerUI provides more functionality, since it paints the layer to the double buffer image, it allows filtering the image with the BufferedImageOp subclasses. With it you can invert colors of a layer, blur it apply any other fancy effects.

For the Rainbow project I used BufferedLayerUI to implement the image editor dialog.
(Here are some more Rainbow screenshots)

Let's see how to apply the grayScale effect to a layer:

        // wrap you component
        JXLayer<JComponent> layer = new JXLayer<JComponent>(myComponent);

        // here we use BufferedLayerUI which can work with BufferedImageOps  
        BufferedLayerUI<JComponent> bufferedLayerUI = new BufferedLayerUI<JComponent>();
        
        // create a ColorConvertOp to apply grayScale effect
        BufferedImageOp grayScaleOp = 
                new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);

        // create a BufferedImageOpEffect with the provided BufferedImageOp 
        BufferedImageOpEffect imageOpEffect = new BufferedImageOpEffect(grayScaleOp);
        
        // set BufferedImageOpEffect to the bufferedLayerUI
        bufferedLayerUI.setLayerEffects(imageOpEffect);
        
        // set the bufferedLayerUI to the layer 
        layer.setUI(bufferedLayerUI);

        // add the layer as a usual component
        frame.add(layer);

And now is the very important news: with JXLayer 3.0 you can catch layer's input events as easy as implement a custom painting. AbstractLayerUI introduces several methods: processFocusEvent, processMouseEvent, processMouseMotionEvent, etc...
The names of the methods speak for themselves. If you'd like to catch all mouseEvents for all components inside your layer - just override processMouseEvent in your custom AbstractLayerUI subclass.

MouseDrawingDemo combines painting and mouse events processing together.

In this demo you can paint over the layer with your mouse, just like you do in any other image editor.
Note that components are alive when you paint over them.

I hope this short overview will help you to invent your own ways of using JXLayer in your projects.
See you one the JXLayer forum

With best wishes
alexp



Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds