Archive for the ‘swing’ Category

Custom Alpha Compositing

March 27th, 2007 | Carl Freeland

Every so often (can’t be more than once every two or three days), Swing doesn’t quite do what we need, and we end up writing customized code. In this case, all the available AlphaComposite instances provided with Java were variations on the theme of combining the colors and alpha channel of both source images into a target image. (Wikipedia’s Alpha Compositing article is good background on the topic).

What if what you really wanted was the color from one image and the alpha channel from another? You’d be out of luck, but for the talents of Brien. Here’s what you normally get with a standard AlphaComposite.SRC_OVER sort of technique. In the following two examples, the icon is opaque and the rectangle is partially opaque black fading to transparency.

AlphaComposite.SRC_OVER

What we needed looks more like this:

SourceAlphaComposite

Read on to find out how we did it, and why. Read the rest of this entry »

Add speel checking to your Swng text components (the squiggly way)

February 12th, 2007 | Brien



web start | download source

Marking up txt

Let’s hook Swing text components up to some tokenizing logic: a spell checker (the example above uses Jazzy), a regex (the example above will pick out some electronic musicians), or something more advanced.

Like all Swing components, text components are factored into an MVC setup. The model is javax.swing.text.Document; the view is javax.swing.plaf.TextUI (which delegates out to a javax.swing.text.View, which is generated from some ViewFactory); and the controller is the text component itself. A very simple way to add the notion of a token to this setup is to create a new kind Document – a TokenDocument.

When text is inserted into a token document, not only does it need to be tokenized, but existing tokens need to be shifted. We could do this manually; however, all javax.swing.text.Document provide something called a sticky position (javax.swing.text.Position) that that will do the shifting for us. Sticky positions are automatically updated by the document to reflect insertions and deletions of text. They also are guaranteed to maintain their ordering – that is, if position A is <= position B, it will always remain that way. This means the token document can maintain sorted trees of sticky positions (to store tokens) without worrying that their sort order will change.

Once we have the tokens in the model, we need to hook them into the view. We do this through a custom TextUI. It basically does everything a BasicTextUI does except it also paints a token layer underneath the highlights (above the background). In general with the javax.swing.text package, whenever code start painting outside of the view bounds (for each offset, this is the tightest bounding box for the letter at that offset), the dirty region needs to be expanded to include everywhere that was painted. In this code, you’ll see a line in the UI to deal with the dirty region.

Playing wth lines

Custom strokes like the squiggle stroke (above left) and smoothed noise stroke (above right) help give meaning to lines. Also, they can make an interface more fun.

Wrap pu

In this code, we extended the UI to paint lines under text. To change the display of the text itself, we would have to write our own View implementation (or more likely, extend PlainView [0]). This is not exclusive of the approach we took here. A more powerful View implementation could work in tandem with our custom UI, opening up even more ways to present information extracted from user-entered text.

Until next time!

[0] There’s a great introduction to this at Customizing a Text Editor, an article on the Sun Developer Network.

LICENSE — I wrote this using the Jazzy spell check engine + some open source trinkets (especially a Perlin noise generator). Except Jazzy (which is LGPL’d), all of it is Apache/BSD licensed.

Swing hack: making transparent components disappear

January 10th, 2007 | Brien

We encounter overlapping components a lot in our application, in situations ranging from a huge drag-and-drop panel across the main frame to small, unavoidable collisions in some of our smaller components. These overlaps almost always involve a transparent component that is used to paint something cool to the screen. Since the final layout looks visually good, it’s easy to think Swing is feeling good. However, this usually isn’t true.

Even though a component is transparent, Swing may still think it occupies some area. You can test this by creating a large transparent panel over a text box and calling SwingUtilities.getDeepestComponentAt(frame, textbox_midx, textbox_midy). You’d expect it to return the textbox; however, it actually returns the transparent panel! Whoa, that’s kind of silly, but what does it matter? The most noticeable loss is the cursor of the textbox. The cursor of the transparent panel will show up instead, even though it’s visually wrong. Other losses would be anything that relies on getDeepestComponentAt, findComponentAt, etc. to return an accurate result.

So how do we make a transparent component disappear to Swing? We were using the hack described by Alex Potochkin in “A well-behaved GlassPane,” for a while, but it turns out things like Container.findComponentAt() actually use Component.contains(). Setting it to a constant false has the effect of hiding all child components as well as the transparent component itself. A better Swing hack might be one that reports a point (x, y) contained in a parent component if it is contained in any child component. That’s what I have below. Just override JComponent.contains() and you’re set!

Here’s a code snippet illustrating the point:

  @Override
  public boolean contains(final int x, final int y) {
   return childrenContains( this, x, y );
  }

  /**
   * Tests whether any of the children in basec contain the given point
   * (in basec’s coordinate system). This does not call
   * basec.findComponentAt or basec.contains.
   */
  public static boolean childrenContains(final Container basec, final int x, final int y) {
    synchronized ( basec.getTreeLock() ) {
      final int size = basec.getComponentCount();
      for ( int i = 0; size > i; i++ ) {
        final Component c = basec.getComponent( i );
        if ( null != c ) {
          if ( c.contains( x - c.getX(), y - c.getY() ) ) {
            return true;
          }
        }
      }
    }
    return false;
  }

Palantir