Showing posts with label swing. Show all posts
Showing posts with label swing. Show all posts

Sunday, August 10, 2008

Decorator Update

Thanks to some input from a few users, the AbstractComponentDecorator has been updated to fix a few visual and behavior artifacts.

Get the latest from Sourceforge SVN.

The decorator component allows you to apply arbitrary decorations to a component without affecting its layout or (necessarily) response to events.

This blog has many different examples of usage beyond simple decorations, including drag images and per-component input blocking.

Wednesday, June 27, 2007

Improved Window Shaping

Thanks to contributions from Olivier Chafik and Chris Deckers, the overhead for setting a window shape mask has been reduced by about two orders of magnitude. In this case, the balloon tip windows no longer have such a noticeable delay before showing.

Linux users should also have fewer issues with this version; some of the library setup previously required is now taken care of automatically by the JNA library.



Saturday, May 12, 2007

Easier Alpha Masks

The per-pixel alpha masking in this post has been codified into a simple API.

WindowUtils.setWindowTransparent(Window w, boolean transparent);

This effectively gives the window a transparent background. The alpha levels of the window's contents are preserved.

The demo is similar to the previous one but adds a few standard components to the mix.



UPDATE If you have a linux system and this for some reason doesn't work, please post a comment to that effect, or post a message to users@jna.dev.java.net so we can ensure this works reliably across all linux systems (64-bit is in the works).

Wednesday, April 18, 2007

Alpha-mask Transparency for a Window

Finally got per-pixel alpha mask working on w32. The included image isn't the greatest for showing an antialiased edge (it does have one), but if you have something with a drop shadow lying about, just drop it on the demo window and that'll be used instead.

Update This demo works on Windows, OSX and Linux (Linux requires JRE1.5+).



Update Here's another image to drop on the demo window, which better demonstrates the alpha blending, in case you don't have any handy


I've got some working X11 code, too, but I need to figure out a decent API to make it happen. OSX lets you just set the window background pixel transparent and then everything you paint in the window is automatically composited with whatever alpha mask you paint with. That could probably work with Swing as well, but you have to magically drop anything with a default background. OSX does this by checking for a magic UIResource color; anything painted with that color is fully transparent.

Update Source is available on BRANCH_V2 from JNA.

I realize I may be an utter dolt, but I find it really hard to follow Microsoft APIs. I've worked with Qt, X11/Xt, and Mac, each of which has its peculiarities of architecture, but those have a consistency (might I say design) that spans more than two or three functions.

Friday, April 13, 2007

Java Transparent Windows (X11 update)





Update: If your X11 setup supports it (XFCE, metacity + xcompmgr, compiz, etc), the JNA shaped window demo now has window transparency.

Thanks to Romain Guy for some initial testing and VMWare for making experimental linux installations easier.

Update: BTW, the linux setup I used was ubuntu (after installing xubuntu packages to get xfce; should have started with xubuntu). Note that the transparency is similar to that on OS X and w32, i.e. a single alpha level for the entire window. Next up is to apply a per-pixel alpha mask, if I can scrounge up some samples of how to do it on each platform.

I added some features to JNA to facilitate working with the X11 visual lookups (it returns an array of structures), so a few more files got changed than just the examples jar. If you want the latest and greatest, you'll need to check out from CVS and do 'ant examples-jar'.

Thursday, April 05, 2007

Another thing that sucks about applets...

You can't easily make the background color match the web page. You'd think that Applet.setBackground() would just Do the Right Thing.

You don't necessarily want to hard-code a value into the applet, but even if you set a value via javascript/livescript, you've got to do extra work to make it propagate down the hierarchy.

This is also applicable to Swing; you can't set a background color on a parent component and have it propagate to descendants. The first non-opaque component you run into is going to get its background from the UIManager. I really don't want to have to figure out which UIManager colors I have to override just to set the bloody background color of my app.

Maybe the new JSR 296 will do something about this, maybe not, but it'd be nice to have something akin to the old X toolkit's resource specifiction:

*.background: red
*.JCheckBox.background: red
JApplet.*.background: red
named-applet.*.background: red

The idea is that you can isolate or aggregate just about anything in the hierarchy by name or by class, and apply a resource setting to it (I guess they're calling it "injection" these days).

Animated Per-panel Options Pane

Here's another example in the search for an unobtrusive property editor. This one looks a little like the Google Maps thumbnail navigator (at least with respect to its activator).


Contents




The API is simple:


// This component's preferred size will normally be that of its content
container.add(new OptionsPanel(content, options));


The purpose of this component is to unobtrusively associate a nontrivial set of options with some content whose display is typically larger than the options themselves. The options display is kept close the the content it modifies, which avoids the user having to go search through menus or play dialog positioning games. Since the options are hidden by default, you also don't have them always using up screen real estate. This sort of thing is often wedged into a docking solution, which is inappropriate if the information doesn't need to be visible at all times. A docking solution also insists on allocating the whole edge of the dock, so if you don't have other junk that has to be in there, you're wasting a lot of space.

The OptionsPane component wraps the content in the center of a BorderLayout and keeps the PAGE_END slot available for the options themselves.

Toggle Button Positioning


Notice that the toggle button isn't affected by the border layout (or any other content, for that matter). The toggle button has been placed in a layer above the rest of the content, so it's always visible and doesn't contribute to any layout complexity.

JRootPane root = SwingUtilities.getRootPane(this);
if (root != null) {
layered = root.getLayeredPane();
if (layered != null) {
int layer = JLayeredPane.DEFAULT_LAYER.intValue();
layered.add(toggle, new Integer(layer + 1));
updateButtonLocation();
}
}

We also want to update the button location whenever the OptionsPane moves. While we could use a ComponentListener, that would update the button's location after the OptionsPane was moved/resized, resulting in some jerkiness to the display. So instead, the button gets moved at the same time as the parent component:

public void setBounds(int x, int y, int w, int h) {
super.setBounds(x, y, w, h);
updateButtonLocation();
}


Layout Animation


Rather than instantly making the options visible and doing a single relayout, we use a javax.swing.Timer to gradually grow the options from the lower right corner into its final position. The bottom slot of BorderLayout will respect a component's height, but stretch or squash the component to the pane's current width. So changing the height gradually will work, but changing the width will have no effect (the component simply appears to rise from the bottom of the panel, rather than from the lower right).

Rather than changing the width, we use an empty border in a JPanel wrapped around the options component. By changing the width of the left border, the options appear to grow from the left to the right.

The animation increment is simple (move half the remaining distance), which is pretty good for most purposes. This is the Timer configuration in response to the expansion button click:

final int INTERVAL = 50;
final int FRACTION = 2;
final boolean expanded = isExpanded;
Timer timer = new Timer(INTERVAL, new ActionListener() {
public void actionPerformed(ActionEvent e) {
Insets insets = optionsBox.getInsets();
Insets delta = targetInsets(expanded);
Dimension targetSize = targetSize(expanded);
int dx = (delta.left - insets.left)/FRACTION;
if (dx != 0) {
insets.left += dx;
optionsBox.setBorder(new EmptyBorder(insets));
optionsBox.revalidate();
repaint();
}
int dy = (targetSize.height - optionsBox.getHeight())/FRACTION;
if (dy != 0) {
Dimension size = optionsBox.getSize();
size.height += dy;
optionsBox.setPreferredSize(size);
optionsBox.revalidate();
revalidate();
repaint();
}
if (Math.abs(dx) <= 1 && Math.abs(dy) <= 1) {
((Timer)e.getSource()).stop();
toggle.setEnabled(true);
if (!expanded) {
remove(optionsBox);
}
revalidate();
repaint();
}
}
});
timer.setRepeats(true);
timer.start();

You can play around with the FRACTION and INTERVAL constants to see the effect it has on the animation.

Source is available in the applet jar file in the article link.

Monday, April 02, 2007

Speech Bubble Update (with Drop Shadow)


I added a drop shadow to the speech bubble. You don't need it on OSX (OSX provides one automatically), and I don't have compiz or similar compositing window manager installed on linux at the moment (although if anyone is willing to submit/test the corresponding code, I'm happy to include it).

Nothing fancy, just black with 50% alpha. The shadow mask is just the original mask, sheared, scaled, and offset. The drop shadow would probably look nicer with a blurred edge, but that likely requires a variable alpha setting for the window (although you might be able to fake it by varying the edge color). Applying per-pixel alpha masks prior to Windows Vista seems to be non-trivial (this example uses a single alpha value for the entire window).

The JNA function definitions are trivial:


int GetWindowLongA(Pointer hWnd, int nIndex);
int SetWindowLongA(Pointer hWnd, int nIndex, int dwNewLong);
boolean SetLayeredWindowAttributes(Pointer hwnd, int crKey,
byte bAlpha, int dwFlags);


As is the actual usage:


Pointer hWnd = getHWnd(w);
User32 user = User32.INSTANCE;
int flags = user.GetWindowLongA(hWnd, User32.GWL_EXSTYLE) | User32.WS_EX_LAYERED;
user.SetWindowLongA(hWnd, User32.GWL_EXSTYLE, flags);
user.SetLayeredWindowAttributes(hWnd, 0, (byte)(255*alpha), User32.LWA_ALPHA);


The only real trick is that DirectDraw must be disabled in order to get the transparency effect and avoid leaving behind painting artifacts.

java -Dsun.java2d.noddraw=true ...

NOTE: thanks to l2fprod for the hint and the original round, transparent clock!

Update: Demo is now available from the JNA homepage.

Wednesday, March 28, 2007

Give your application a speech bubble

As a result of an argument about the best way to present meta-information about complex application objects presented to the user, I had to come up with some concrete alternatives.

The problem is that for a given item in the application (in this case an SVN-based revision control client that aggregates and presents large amounts of meta-information about the repository), there may exist information that just doesn't fit in the display. You want to provide the user with a route to the desired information with as few inputs as possible.

Tooltips is one alternative, but aren't always appropriate. While you can set (globally) the delay before appearance and the time before dismissal, dismissing them is implicit. In my application, I need the meta information to be displayed or hidden explicitly, so that rules out tooltips (at least without substantial modification of the tooltip manager).

A dedicated "inspector" window is another alternative, but kind of heavyweight. We definitely don't want a component that can be activated, since that introduces focus issues. I also didn't want to make it appear to the user that there was a generic "container" that kept resizing and moving, as if it were a tool palette that couldn't decide on its purpose. At a minimum the window has to be undecorated and pretty much behave like a tooltip.

What I really wanted was something in between the two. A simple Popup with a unique appearance which could be shown or hidden at will. Apple introduced something like this as Balloon Help years ago, although I think the main problem was that they didn't get the trigger quite right. One reason a lot of expert users don't like tooltips (or balloon help) is that they show up when you don't expect it and you have to wave the mouse to make them go away (usually causing another one to be triggered).

Google maps does a nice job with their balloon tips. The trigger is obvious, and the tip itself provides a close button (and now I notice a maximize button as well).

So I started out with Popups via PopupFactory and wound up with another demonstration of non-rectangular clipping, brought to you by the JNA project. The API is pretty simple:


Component myComponent;
Component myBubble;
Point where; // location relative to myComponent
final Popup popup = BalloonManager.getBalloon(myComponent, myBubble, where.x, where.y);
popup.show();
// Hide the popup on mouse clicks
myBubble.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
popup.hide();
}
});


The balloon is a simple rounded rectangle with a triangle stuck to the bottom, with the corresponding Area used as the window mask.

I originally tried using PopupFactory directly, but it doesn't provide enough control over the intermediate components (the ancestors of the content that you don't really see; there's a pesky medium-weight popup that uses java.awt.Panel, which can't be easily masked). This implementation uses a window for any popup, regardless of whether its fully contained within the parent component.

Drop shadows would be nice (maybe someone wants to contribute some compositing code?).




Tuesday, February 27, 2007

Non-rectangular Windows Update




Updated to include support for OSX and linux.

While OSX doesn't actually use any native code to do the window mask, JNA support is there if it needed to. Source (and per-platform binaries) is available at http://jna.dev.java.net.

Update
Transparent version of the demo is available here.

Friday, February 23, 2007

Non-rectangular Windows

I've been meaning to play around with shaped windows for a while, but didn't relish the thought of walking through the tedium of JNI configurations and builds. Doing it on one platform is bad enough, but on several? No thanks.

So I thought I'd write a little scriptable shared library access stub once and be done with JNI entirely. Well, turns out it's already been done. Several times.

JNative
NLink
JNA

JNative has some interesting features not found in the others, but actually using it is only slightly better than JNI. NLink is currently w32-only, and has a bit of a COM bent. JNA fit most closely with my objectives, already had implementations for w32 and solaris, and was already platform-agnostic. So I took a couple days to hack in some more features (mostly to get an understanding of the codebase), and here is what I got. The following code is what it takes to make a frame take an arbitrary shape.

User32 user32 = User32.INSTANCE;
GDI32 gdi32 = GDI32.INSTANCE;
JFrame frame = new JFrame(getName());
Pointer p = gdi32.CreateRoundRectRgn(0, -150, 300, 300, 300, 300);
int hWnd = user32.FindWindowA(null, getName());
user32.SetWindowRgn(hWnd, p, true);

Looking up the appropriate w32 calls probably took the most time. How are the w32 libraries defined? How is this for trivial:

public interface User32 extends StdCallLibarary {
User32 INSTANCE = (User32)Native.loadLibary("user32", User32.class);
int FindWindowA(String winClass, String title);
void setWindowRgn(int hWnd, Pointer p, boolean redraw);
}
public interface GDI32 extends StdCallLibrary {
GDI32 INSTANCE = (GDI32)Native.loadLibrary("gdi32", GDI32.class);
Pointer CreateRoundRectRgn(int left, int top, int right, int bottom, int w, int h);
}

Now, somebody's probably going to point me to how SWT has had this functionality for years (does it?), but this is nicely abstracted and based on a very small number of classes. I'll be updating the code at jna.dev.java.net (or maybe opening a new location if I can't get the existing project moved to subversion), but for now, check out the demo by clicking on everyone's favorite orange launch button.




Permissions required, because this runs some custom native code.

Oh, BTW, this is windows-only for the moment. I'll do X11 next and anyone's free to send me some code snippets for setting window masks on other platforms. I could also use some help porting to PPC and other platforms (a very small amount of native ASM to push arguments to the stack, not too hard).

Monday, November 27, 2006

Waiting and Blocking Input

Those pesky users are always trying to submit forms more than once, or creating new files willy nilly while they're waiting for feedback.

This article addresses two strategies for avoiding Impatient User Syndrome. First, provide some feedback to show that the program is busy, and second, don't allow any additional input.

Now, Java provides a JProgressBar, which you can throw up in a Dialog, but those aren't always appropriate. Sure if you want to block all application input (or in the case of Java 1.6, all frame input), that'll work, and you can always disable one component at a time. But suppose, for instance, that you've got a single frame, with a couple of different sections or docked panels. If their operation is independent of one another, you certainly don't want to block them all with a dialog.

The following demo shows how to block input on individual components, groups of components, or an entire frame, as well as providing "busy" feedback. The class WaitIndicator installs a component in the JLayeredPane above the target component, which prevents any mouse input from reaching the target component, and displays an hourglass cursor over the target component. It also installs a key event handler to ensure that no key events reach the target component.

The WaitIndicator also provides a paint method, which you can use to provide whatever visual feedback is appropriate to your program. The default implementation fades to the target component's background color. The demo itself uses a SpinningDialWaitIndicator, which paints an Apple-like spinner (which you can also see in the animated icon demo applet a few entries back).





Source

Tree (and tree table) with checkboxes

When the user needs to select a non-trivial number of items from a hierarchy, normal tree item selection is too prone to losing the selection through mistaken clicks. The standard solution to provide a more robust selection method is to include check boxes next to each selectable item in the tree.

Hat tip to some previous online implementations (each has a different implementation):

Bare bones

With a TreeCellEditor

More recent variations


After a couple of iterations of checkbox trees (and tree tables), I rolled this up into a standalone TreeCellRenderer so that it can be applied more easily to any tree-oriented component. This implementation also properly handles rollover effects on the checkbox, which is lacking in the above implementations (most visible under w32; Java's Metal LAF has no checkbox rollover effects).

The checkbox painting is handled by using a real checkbox and painting its contents into an icon. The state of the checkbox is set just prior to painting to ensure the proper rollover state. The original cell renderer's icon is replaced with a composite icon of the original plus the checkbox (hit ^-shift-O to switch component orientation).

The renderer provides the current checked selection as an array of TreePath or an array of int representing the rows.

I also applied this to my own tree table implementation (similar to SwingX JXTreeTable) by just overriding a few protected methods which translate mouse coordinates into rows/paths.




Source

Tuesday, October 10, 2006

Iconic Inversion

Here's a problem and solution described in an applet.






The goal here is to make the icon look consistent with the selected text, which means using the JList's selection fg/bg colors where the icon uses the component's normal fg/bg colors.

This applet uses a SelectionIcon, which applies a custom composite to the original icon. The custom composite looks for the list fg/bg colors in the original icon (and some colors "nearby"), and swaps them out for the selection fg/bg. It also applies a slight tint based on the selection background.

The SelectionIcon can be applied to any existing icon. It's useful for selections because you don't know a priori what your selection colors are, so it's hard to make a dedicated selection version of your original icons that will look good in all cases. Assuming that the selection fg/bg are set reasonably, your icon will look at least as good as the selection text. The high contrast colors in the above example are from Win2k, but you never know what the user may have set.

Source

Friday, September 15, 2006

Animated Icon Redux

Lately we've been putting animated icons all over the UI to provide feedback about actions that are in progress. The spinning arrows are an animated GIF, while the spinning dial is dynamically generated with Java2D. Both icons are used just like any other icon (set and forget). No special coding is needed to get them to animate. This is how animated icons should behave.






The Icon interface's paintIcon method carries enough information for the icon to trigger its own repaint when it needs to display the next frame. Internally, the spinning arrows icon tracks the component on which it was painted as well as the location. Using this information, each time we want to display a new animation frame, we just walk the list of repaint locations and request a repaint on each of the stored items.

After testing this method out on a variety of components, I realized that this would be a superior method of animating icons in trees, tables, and lists (or any other component that makes use of CellRendererPane. With just a few tweaks, the logic was refactored to wrap animated GIF icons so that when they are used within a tree, table, or list, they are automatically refreshed as necessary. This is a much cleaner solution than the one I posted earlier.

I have a generic icon loader for my apps, so if the loader detects an animated GIF, it automatically wraps it in an AnimatedIcon and I don't have to worry about manually tracking animations.

Source

Wednesday, August 30, 2006

Swing Drag Images, Improved

Performance is improved, generating the drag image once instead of repeatedly.




Source

Saturday, August 12, 2006

Drop Target Navigation, or You Drag Your Bags, Let the Doorman Get the Door

When I'm dragging a 75lb bag and a five year old, it's rather nice to have someone open the door. When I'm dragging things around in my application, it's also nice not to have to open all the doors on the way to my destination before I start dragging.

Specifically, I've got a set of forms in an instance of JTabbedPane. I know one of the tabs holds a field that defines my preferred download directory, which I happen to have open in my file browser. Drag from the file browser onto the JTabbedPane, and -- oops, I'm not on the right pane. It'd be nice to have the tabs change as I drag across them so that I can find the right destination.

Since this is system-wide behavior, I'd like to enable the behavior once at application startup and forget about it, rather than having to add listeners or subclass every instance of JTabbedPane and JTree that's going to accept a drop.

DropTargetNavigator.enableDropTargetNavigation();

In order to make this happen, we install a global listener to the default DragSource, which lets us respond to any drags initiated within the same VM. Since that provides us with the current drag location, we can scan through the currently available components using Frame.getFrames() and SwingUtilities.getDeepestComponentAt() to see if what's under the cursor needs to be navigated. If we do make a modification, a Runnable which will undo the modification is added to a queue so that we can restore the component to its original state if the drop ends up going somewhere else or being canceled.

To handle drags originating from outside the VM requires a little hacking, since there isn't any sort of global drop target listener. During a native drag, you won't see any MouseEvents. As of 1.4+, there is a MouseEvent (actually a SunDropTargetEvent) that gets processed by Component.dispatchEventImpl(), but it gets swallowed before the component passes events off to AWTEventListeners. You can't override dispatchEvent, and wouldn't want to anyway because you'd have to do it on every component. Instead, we can install a custom EventQueue which can watch for the drop target events and forward them to our drop target listener.

The navigator class has built-in support for JTabbedPanes and JTrees. Custom classes that wish to support navigation can register themselves with a simple interface that performs custom navigation and rollback. This way you can enable auto navigation on whatever tree table implementation you happen to be using.

In order to avoid unintended navigation when the mouse is dragged rapidly across a component, I've added a configurable delay to when the navigation starts. You need to hover over the same spot for roughly half a second to activate the navigation.


Tuesday, August 08, 2006

Mighty Mitochondria! Them Cells Are Animated!

Java provides effortless loading and display of animated GIFs. Just tell ImageIcon where it can find the file, then plug the ImageIcon into a JLabel or JButton. Couldn't be simpler.




Now how about putting that animated icon into a JTree as feedback for loading status.





Oops. What happened? Did we get the right icon? Why isn't it animated? Try selecting one of the tree nodes, then rapidly move the selection up and down. Watch what happens.

What happened is that no one told the JTree it needed to repaint itself after it painted the initial image. Selection changes trigger repaint messages, which is why the icons move a little bit when you move the selection around. Any animation is driven by regular repaints of the component where it is drawn. JLabels and JButtons are no exception. Take a look at the source for ImageIcon.paintIcon:

public synchronized void paintIcon(Component c, Graphics g, int x, int y) {
if(imageObserver == null) {
g.drawImage(image, x, y, c);
} else {
g.drawImage(image, x, y, imageObserver);
}
}

When you create your ImageIcon, it doesn't have an image observer, so the component where it's being drawn is used instead (note that java.awt.Component implements ImageObserver):

public boolean imageUpdate(Image img, int infoflags,
int x, int y, int w, int h) {
int rate = -1;
if ((infoflags & (FRAMEBITS|ALLBITS)) != 0) {
rate = 0;
}
// ...
if (rate >= 0) {
repaint(rate, 0, 0, width, height);
}
return (infoflags & (ALLBITS|ABORT)) == 0;
}

So whenever the image reports that it has another frame ready (infoflags&FRAMEBITS != 0), it calls this method.

That's just fine for a label or button which only displays a single icon, but what about a tree, which might display this icon in every row? We need an image observer which can call repaint with the cell bounds of every cell that contains the animated icon. Using the tree itself as the observer could work, but we'd end up redrawing the entire tree on every frame. It's preferable to use a decorator which keeps track of which individual cells need updating.



In this case, let's use the tree cell renderer as the entry point or trigger to start or stop animation, since that's normally where a tree's icons get customized.


JTree tree = ...;
TreeCellRenderer r = new DefaultTreeCellRenderer() {
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean selected, boolean expanded,
boolean leaf, int row, boolean focused) {
Component c =
super.getTreeCellRendererComponent(tree, value, selected, expanded,
leaf, row, focused);
TreePath path = tree.getPathForRow(row);
if (isLoading(path.getLastPathComponent()))
setIcon(LOADING_ICON);
// The following line is what you'd normally use; the rest is just illustrative
//CellAnimator.animate(tree, c, path);
// Let the CellAnimator utility figure out if there's an animated icon here
ImageIcon icon = (ImageIcon)getIcon();
if (CellAnimator.isAnimated(icon)) {
CellAnimator.animate(tree, new CellAnimator.TreeUpdater(tree, row), icon);
}
else {
CellAnimator.stop(tree, row);
}
return c;
}
};
tree.setCellRenderer(r);


The CellAnimator needs to do several things. At any point if it is determined that no animation is required, the animation support is disposed for the current location.

  1. Obtain the icon in use, if any
  2. If the icon is animated, install an ImageObserver
  3. Identify the location to receive repaint notifications, and add it to the observer's list


Whenever the animated icon posts an update, the ImageObserver for that icon will walk its list of repaint locations and trigger repaints. The actual implementation takes care of a number of other details, like using weak references for UI components and remove locations if the location or component is no longer valid.

The CellAnimator provides one-line methods to enable animation on standard Swing components and is easily extensible to enable animation on custom components that use the cell renderer technique.

Here is a demo of the cell animator applied to a lazy-loading tree.


The source includes JUnit-based unit tests which use the Abbot library.

Friday, August 04, 2006

UI Testing on the Sly

One of the drawbacks of testing your UI components (you are testing them, right?) is that to properly run a test, it needs to run on a display. Unfortunately, if you want to use that display for something else (like writing code and fixing bugs), you run into problems with either the tests writing your code, or the code you're writing supplying input to your UI tests.

One good solution is to have a dedicated machine for the tests, which you can continually monitor and can easily be updated with changes from your dev machine. Since that isn't always an option, I'll present a few alternatives for several different platforms which allow you to run UI tests without having a separate machine to run them on.

OS X


VNC is a great tool for controlling remote computers of any platform. If you have fast user switching enabled, you can actually use OSXvnc to display the desktop for a user other than the one currently using the display. If you configure OSXvnc to not quit when switching to another user, you will always be able to connect to the desktop where OSXvnc was first launched.

Set up a "test" user, and set up OSXvnc to launch when that user logs in. Set up the "Startup" tab like this:

Once OSXvnc is running, you can access the "test" desktop while using your normal desktop. Run your tests in the background, peek in on them to check the results.

Linux


Run vncserver, either manually or as a daemon. This will give you an additional X display which is totally separate from your physical one. Connect to the new display with vncviewer, and you have a window onto your "test machine".


Windows


Windows isn't quite as functional when it comes to sharing a display, but you can still get some mileage out of UI testing on the same machine. There's a peculiar little mode in windows, I'll call it "service mode", where you think you have a display but you don't. This prevents java.awt.Robot from working properly, but you can still get some mileage from UI tests that don't depend on screen captures or native drag and drop. Abbot (a UI testing library) will work just fine in this mode, and does so by artificially generating events directly into the AWT event queue in order to drive a UI.

The easiest way to run tests in this manner is to install cygwin sshd. It runs as a service, so by default it doesn't have access to the desktop (there is a checkbox in the services admin control panel that indicates whether a service is allowed desktop access). Once that is installed and configured (google "cygwin sshd install"), you can use ssh to get a shell prompt within the service mode "sandbox". If you've got an ant script to run your tests, just invoke it. Abbot automatically detects whether this mode is in effect, so if you're using that library, everything works out of the box. If you only run your tests interactively through an IDE, you're out of luck, since you won't be able to interact with the IDE if you launch it in service mode.

Monday, July 17, 2006

Drag Images for Everyone...and we do mean Everyone

You may have seen Sun's tutorial on using Swing's built-in drag and drop support. One of the demos from the tutorial looks like this:



Today's demo puts a drag image on each of the displayed drag-enabled components, and does so without modifying the original jar file. It also does so without any special knowledge of the components that are drag enabled. (Web start doesn't let you reference a file on another host, so you'll have to trust me when I say the original jar is used unmodified).



A few sights to take in after you've checked that "Turn on Drag and Drop" checkbox.


  • Try a drag from each component.
  • Try dragging a multiple, discontiguous selection from each component.
  • Watch what happens when your drop is rejected.
  • Move the main window, and drag some more. Notice the nice images across both windows (the original main has simply been called twice).


Source Here.

The technique depends on all drag sources to use the default DragSource as the drag initiator. To date, all drags implemented within the JRE use the default, as do all the drag & drop examples I've seen on the web, so it always works in practice.

The global drag source listener technique used here could also be used to automatically switch the visible tab in a JTabbedPane, expand a node in a JTree, or even autoscroll components that don't implement Autoscroll. Watch for an upcoming article to address those options.