Auto-scrolling in Swing applications

March 21st, 2008 | 8 Comments »

Auto-scrolling is a feature that can be found in most mainstream modern applications. Pressing the middle mouse button transfers the application into the auto-scrolling mode. In this mode, moving the mouse down starts scrolling down the contents even when the mouse is no longer moving. The scroll speed depends on the distance between the current mouse location and the mouse press location. This works for all four directions (up, down, left, right).

There are two existing Swing implementations that i’m aware of:

Both approaches have their drawbacks. The first one effectively changes the UI hierarchy and the second one uses a global frame resource, effectively resulting in repainting the entire UI on every scroll (even when the scroll pane is a small part of the application).

Christopher Deckers has already appeared as a guest author on this blog, and he was very helpful during the development cycles of last two versions of Substance look-and-feel. He has found numerous bugs (many thanks) and made numerous suggestions for enhancing existing features and adding new ones (many thanks). One of his “most wanted” features was the support for auto-scrolling, and after a few iterations he has contributed an elegant implementation that does not use a global resource and does not change the UI hierarchy.

The new LafWidget.AUTO_SCROLL client property can be installed on a specific scroll pane or globally on the UIManager. Once it is set to Boolean.TRUE, the corresponding scroll pane(s) will have the auto-scroll mode installed on them. There are two operation modes:

  • Press the middle mouse button and release it. The scroll pane is transferred into the auto-scroll mode. It can be dismissed by clicking the mouse or scrolling the mouse wheel.
  • Press the middle mouse button and start dragging. The auto-scroll is dismissed by releasing the middle mouse button.

The implementation itself installs a mouse listener on the scroll pane, and when the middle mouse button is clicked, it shows the auto-scroll icon wrapped in a popup menu (thus no need for glass pane). In addition to showing the menu, it also installs a global AWTListener that listens to mouse wheel and mouse move events, scrolling the pane as necessary.

This is the last new feature that was added to the version 4.3 of Substance. It is now in feature freeze state. The release candidate is scheduled on March 31st, with the final release scheduled on April 14.


Related posts:

  1. Demo for auto-scrolling in Swing applications The previous entry on auto-scrolling in Swing applications under the latest development drops of Substance...
  2. Animation blueprints for Swing – scrolling layout After adding such animation effects as fading, translucency, load progress and asynchronous load of images...
  3. Animations 202 – scrolling After seeing how the rules of physical world can be applied to animating colors, it’s...
  4. New Creme Coffee skin for Swing applications Just before the feature freeze of Substance 4.1, i added a new Coffee Cream skin...


8 Comments on “Auto-scrolling in Swing applications”

  1. 1 Alexander Potochkin said at 2:29 pm on March 22nd, 2008:

    Hello Kirill

    It is interesting that I started my own implementation for this feature
    about a month ago, so you are always a step ahead of me :-)

    By the way, could you give more details about why glassPane approach
    “repaints the entire UI on every scroll ”

    What do you mean by the “entire UI”, is it the whole frame ?

    Thanks
    alexp

  2. 2 Kirill Grouchnikov said at 11:47 pm on March 22nd, 2008:

    Alex,

    It was my understanding that a non-opaque glass pane repaints everything (the whole frame) when it needs to repaint. If the specific glass pane implementation does not have any child elements (just overrides paintComponent()), a repaint of any sub-area of that frame will trigger the repaint of the glass pane which will trigger the repaint of the entire frame. Is my understanding of the scenario incorrect?

    Thanks
    Kirill

  3. 3 Alexander Potochkin said at 12:03 pm on March 23rd, 2008:

    Hello Kirill

    I wrote a simple test which shows how repainting is changed when glassPane is visible. I’ll add the sources at the end of this message

    When you move the mouse over the checkBox and glassPane is invisible,
    you can see in the console that checkBox is repainted within its bounds

    By clicking on this checkBox you’ll make the glassPane visible,
    move the mouse over it again and you’ll see that painting starts from
    the RootPane now, goes to the checkBox and finally to the GlassPane

    The painting process of Swing is smart enough
    to quickly finds the origin component to be repainted,
    and painting the small part of the transparent glassPane costs nothing

    As you can see all painting are performed within the bounds of the CheckBox,
    so it is very different from the “repainting the entire UI”

    Would you agree?

    Thanks
    alexp

    import javax.swing.*;
    import java.awt.*;
    import java.awt.event.ActionEvent;

    public class GlassPaneRepainting {
    private static void createAndShowGui() {

    final JFrame frame = new JFrame() {
    protected JRootPane createRootPane() {
    JRootPane rp = new JRootPane() {

    public void paint(Graphics g) {
    System.out.println(“”);
    System.out.println(“JRootPane.paint”);
    System.out.println(“g.getClipBounds() = ” + g.getClipBounds());
    super.paint(g);
    }
    };
    rp.setOpaque(true);
    return rp;
    }
    };

    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLayout(new FlowLayout());

    frame.add(new JCheckBox(“Show the glassPane”) {

    public void paint(Graphics g) {
    System.out.println(“JCheckBox.paint”);
    System.out.println(“g.getClipBounds() = ” + g.getClipBounds());
    super.paint(g);
    }

    protected void fireActionPerformed(ActionEvent event) {
    frame.getGlassPane().setVisible(!frame.getGlassPane().isVisible());
    System.out.println(“”);
    System.out.println(“getGlassPane().isVisible() = ” + frame.getGlassPane().isVisible());
    super.fireActionPerformed(event);
    }
    });

    frame.setGlassPane(new JPanel() {
    {setOpaque(false);}

    public void paint(Graphics g) {
    System.out.println(“GlassPaneRepainting.paint”);
    System.out.println(“g.getClipBounds() = ” + g.getClipBounds());
    super.paint(g);
    }
    });

    frame.setSize(200, 200);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    }

    public static void main(String[] args) {
    SwingUtilities.invokeLater(new Runnable() {
    public void run() {
    createAndShowGui();
    }
    });
    }
    }

  4. 4 Kirill Grouchnikov said at 2:04 pm on March 23rd, 2008:

    Alex,

    This looks a little surprising to me. What happens when the painting done in glass pane overflows the component bounds (as i did for validation overlays)? When i need to update that painting, i’ll have to force repaint of the entire frame myself?

    And look at the example from Santhosh – the scroller image is drawn centered around the mouse click location. While the test application that he shows only has one scroll pane and he clicked in the middle of that scroll pane, what would happen if you click in the top left corner of the scroll pane? Would the scroller image be cut? And if it would, would you have to force the repaint of the entire frame?

    Thanks
    Kirill

  5. 5 Alexander Potochkin said at 5:27 am on March 24th, 2008:

    Hello Kirill

    GlassPane is repainted when any component under it is repainted,
    if you place a decoration outside e.g. button and want it to be in sync,
    with the button’s state you certainly need to repaint a glassPane

    but again, one of the key point of the fast painting is not to paint more
    than you need, so in this case you should repaint only small part of the glassPane, which contains that decorations

    About example from Santhosh:

    > what would happen if you click in the top left corner of the scroll pane? > Would the scroller image be cut

    I don’t know, I thought you had tested it :-)

    In my opinion, in the implementation from Santhosh,
    the whole frame is repainted once when
    you press the wheel and the ScrollGlassPane becomes visible

    if you need to move the scrolling indicator,
    you just need to repaint the small area where it was
    and the area where it is now

    I don’t see any reasons why the frame should be repainted during scrolling

    Thanks
    alexp

  6. 6 Kirill Grouchnikov said at 9:01 pm on March 24th, 2008:

    Alex,

    Thanks for the clarifications. I had the wrong idea that the entire frame is repainted once a non-opaque glass pane is installed.

    Kirill

  7. 7 Alexander Potochkin said at 5:34 am on March 25th, 2008:

    You are welcome my friend,
    see you in the J1 soon

    alexp

  8. 8 Alexander Potochkin's Blog said at 9:42 am on June 24th, 2008:

    JXLayer 3.0 – MouseScrollableUI

    Implementing auto-scrolling feature with JXLayer