Skip to content

Dramatically Improved UI in jEdit

22
May
2008

This is definitely old news by now (in fact, almost a month old), but I’m just now discovering it myself so I decided to share.  The jEdit project is renowned for two things:

  • Marvelous support for every language under the sun
  • Eye-bleedingly bad UI design

It’s always been possible to hack yourself an improved version without too much trouble; but by default, jEdit has always looked terrible.  This one factor, more than anything else, has contributed to jEdit’s reputation as the supercharged editor which everyone refuses to try.  Fortunately, this influence has been seriously reduced in the 4.3pre14 release:

image

Compare that to the old look.  Even with Java 6 subpixel rendering, the interface remained a mess.  What’s more, many of the interface elements were custom renderings, preventing the platform-native LAF from appropriately styling them (the toolbar controls are a prime example).  All of this is fixed in 4.3pre14.

jEdit is rapidly approaching “usable editor” status out of the box, something that even the mighty TextMate hasn’t quite achieved.  Granted, it’s still Swing-based, which means the fonts render horribly on Vista without Java 6uN, but it’s a step in the right direction.  Now, if only they would do something about their website

Buildr Still Not Ready for Prime Time

12
May
2008

As some of you may know, I’ve been following the Buildr project with a fair degree of interest of late.  Just last week, Assaf announced their first release as an Apache project: Buildr 1.3.0.  Being of the inquisitive bend, I decided that this would be an excellent time to reevaluate its suitability for use in my projects, both commercial and personal.

A while back, I evaluated Buildr as a potential replacement for Ant and Maven2 and eventually came to the unfortunate conclusion that it just wasn’t ready.  At the time, Buildr was still hampered by a number of annoying limitations (such as being unable to reconfigure the default directory structure).  I’m happy to say that many of these problems have been fixed in the 1.3 release, as well as a large number of feature additions which I hadn’t thought to request.  Despite all that, I’m still forced to conclude that Buildr simply isn’t (yet) suitable as my build tool of choice.

Now before you stone me for utter heresy, try to hear me out.  I’m not dissing Buildr in any way, I really want to use it, but I just can’t justify moving over to it in the face of all of its current issues.  What’s really unfortunate is that all of these issues can be summed up with a single word: transitivity.

One of Maven’s killer features is the ability to resolve the entire transitive dependency graph.  Now I’ll grant that it does this with varying degrees of success, but for the most part it’s pretty smart about how it fixes up your CLASSPATH.  As of 1.3, Buildr claims to have experimental support for transitive dependency resolution, but judging from the problems I encountered in my experimentation, it’s barely even deserving of mention as an experiment, much less a full-fledged feature.

To understand why transitive dependencies are such a problem, it is of course necessary to first understand the definition of such.  In a word (well, several anyway), a transitive dependency is an inherited dependency, an artifact which is not depended upon directly by the project, but rather by one of its dependencies.  This definitions carries recursively to dependent parents, grand-parents and so on, thus defining a transitive dependency graph.  Consider it this way:

image

In the diagram, MyCoolProject is the artifact we are trying to compile.  The only dependencies we have actually specified for this artifact are the DBPool and Databinder artifacts.  However, the Databinder artifact has declared that it depends upon both the Wicket and the ActiveObjects artifacts.  ActiveObjects doesn’t depend upon anything, but Wicket has dependencies SLF4J and on the Servlets API.  Thus, our original goal, MyCoolProject, has transitive dependencies Wicket, SLF4J, Servlets and ActiveObjects.  Quite a bit more than we thought we were asking for when we declared our dependency on Databinder.

In general, this sort of transitive resolution is a good thing.  It means that instead of specifying six dependencies, we only had to specify two.  Furthermore, we observed the DRY principle by not re-specifying dependency information already contained within the various packages.  As with any “good thing”, there’s definitely a valid argument regarding its down sides, but on the whole I’m quite fond of this feature.

In Maven, this sort of thing happens by default, which can lead to some very confusing and subtle CLASSPATH problems (conflicting packages, etc).  Buildr takes a slightly different approach in 1.3 by forcing the use of the transitive method:

repositories.remote << 'http://www.ibiblio.org/maven2'
 
define 'MyCoolProject' do
  project.version = '1.0'
 
  compile.with transitive('xmlrpc:xmlrpc-server:jar:3.0')
 
  package :jar
end

It’s easy to see why Buildr is raising such a ruckus in the build genre.  It’s syntax is elegance itself, and since it’s actually a proper scripting language (Ruby), there’s really nothing you can’t do with it.  Having to remember to specify the default repository is a bit of a pain, but it’s certainly something I can live with.  The real problem with this example is a little less subtle: It doesn’t work.

If you create a buildfile with the above contents and then run the buildr command, the results will be something like the following:

Downloading org.apache.xmlrpc:xmlrpc:jar:3.0
rake aborted!
Failed to download org.apache.xmlrpc:xmlrpc:jar:3.0, tried the following repositories:
http://www.ibiblio.org/maven2/

(See full trace by running task with --trace)

After a fairly significant amount of digging, I managed to discover that this problem is caused by the fact that Buildr attempts to download a corresponding JAR file for every POM it resolves.  This seems logical until you consider that many large projects (including Databinder, Wicket, and Hibernate) define POM-only projects which exist for the sole purpose of creating transitive dependencies.  They’re organizational units, designed to allow projects to depend upon one super-artifact, rather than a dozen sub-projects.  It’s a very common practice, and one which Buildr completely fails to handle appropriately.

After some prodding on the Buildr dev mailing-list, Assaf admitted that this is an issue worth looking at and provided a temporary workaround (pending a rework of the functionality in 1.4):

def pom_only(a)
  artifact a do |task|
    mkpath File.dirname(task.to_s)
    Zip::ZipOutputStream.open task.to_s
  end
end

This was about the point that I started wondering if maybe Ant/Ivy would be a better bet, at least for the time being.  To use this workaround, you must call the pom_only method once for every POM-only project in the dependency graph.  Usually, this means you must invoke Buildr repeatedly and find the troublesome artifacts by trial and error.  Not exactly a “just works” solution.

Pressing forward however, I unearthed a deeper, even more insidious issue: an intermittent failure to generate Eclipse project meta.  I’m not sure if this is due to the POM-only dependencies or just bad juju, but whatever the reason, it’s annoying.  I’ve raised the issue on the Buildr mailing lists, but so far no response.  Basically, what happens is something like this:

C:\Users\Daniel Spiewak\Desktop\MyCoolProject> buildr eclipse
(in C:/Users/Daniel Spiewak/Desktop/MyCoolProject, development)
Completed in 0.499s
C:\Users\Daniel Spiewak\Desktop\MyCoolProject>

Not exactly the most helpful output.  In case you were wondering, this process did not create the Eclipse metadata.  It’s interesting to note that calling buildr idea (to create project metadata for IntelliJ) seems to work just fine.  Whatever causes the bug, it seems to be specific to just the Eclipse project generator.

Buildr is a remarkable project.  It shows potential to someday become the de-facto build system, possibly even unseating Ant.  Unfortunately, that day is not today.  There are too many odd wrinkles and unpredictable errors to really call it a “finished product”.  Hopefully, the Buildr dev team will continue their excellent work, eventually producing a tool worthy of serious consideration.  Until then, I guess that I’m (still) stuck with Ant.

Update: It seems that the issues with transitive dependencies have been resolved in the latest Buildr versions in their SVN.  I’m looking forward to when this all becomes stable and publicly consumable!

Screencast: Introduction to the Scala Developer Tools

21
Apr
2008

Virtually everyone who has visited the Scala project page has seen the info page for the Scala plugin for Eclipse.  There are a few screenshots, an update site and very little instruction on how to proceed from there.  Those of you who have actually installed this plugin can vouch for how terribly it works as well as the remarkable lack of usefulness in its functionality.  It’s basically a very crude syntax highlighting editor for Scala embedded into Eclipse.  It has the ability to run programs and compile them within the IDE, but that’s about all.  Worse than that, it seems to make everything else about Eclipse less stable; somehow crashing random, unrelated plugins (such as DLTK).  Needless to say, it’s often a race to see how fast we can remove the Scala Eclipse plugin from our systems.

What is far less widely known is that there is a second Eclipse plugin which offers support for Scala development.  Basically, the guys at LAMP decided that it wasn’t worth trying to build out the original plugin any further.  Instead, they started from scratch and created a whole new implementation.  The result is entitled the “Scala Developer Tools” (or SDT, if you’re into short and phonetically confusing acronyms).  Basically, this plugin is a very unstable, very experimental attempt to build a first-class IDE for Scala on top of Eclipse.  Obviously, they still have a ways to go:

image

In case you were wondering, no that isn’t my default editor font.  To say the least, the plugin suffers from an annoying plethora of UI-related bugs.  Behavior is inconsistent, and often times changing a value doesn’t seem to be permanent (it took me several tries to get the syntax highlighting to stop shifting before my very eyes).  To make matters worse, it seems that installing the plugin in the first place is a bit like playing a game of hopscotch using un-anchored floats in the middle of a pool.  The update site has a nasty habit of throwing a 404 about 50% of the time.  You know what they say: if at first you don’t succeed…

The good news is that once you get the plugin installed, the preferences beaten into submission, and the UI bugs safely ignored, things become quite nice indeed.  The new editor is vastly improved over the old one, and it’s easy to see tremendous potential in the project.  Things are actually getting to a point where I would consider using the plugin rather than my current jEdit setup.

Of course, it’s hard to get a good idea of how a tool works until you see it in action.  That’s why I took the time to put together a small screencast which illustrates some of the highlights of the new editor.  I made no attempt to hide the bugs which cropped up during my testing, so this should give you a fair approximation of the current state of the plugin and whether it’s worth trying for your own projects.  The screencast has been produced at a reasonably high resolution (1024×732) in both Flash and downloadable AVI format.  Enjoy!

screencast-front

 

Language Choice Overload

20
Feb
2008

I’ve been seeing a surprising trend in the articles that have come through my feed reader recently: evaluating JRuby, Groovy and Scala for use as primary languages in new, on-JVM projects.  This seems to be the new and trendy thing to write about.  I suppose it’s not really surprising that each of these languages is receiving such attention, they all deserve it.  What is surprising is that they are all put on the same playing field.  Of the three, only Ruby and Groovy are actual peers in terms of capability and design.  Syntactic differences aside, both languages do roughly the same things in fairly similar ways.  Scala on the other hand isn’t even in the same league.

This is about the point that most comparison rants conclude by saying “Scala is statically typed therefore it is completely different.  QED.”  We can actually go a bit deeper though.  Scala’s entire philosophy and reason for existence is divergent from dynamic, so-called “scripting” languages like Ruby or Groovy.  Scala is designed to be a mid-level, general purpose language.  By “general purpose” I mean something suitable for writing large applications, enterprise class stuff, etc.   Not suitable for writing operating systems or for creating quickie scripts.  In short, Scala is a peer for Java more than it is a peer for Ruby.

What I think is interesting is that Scala has a number of features which make it appear to be a dynamic language (such as implicit conversions, flexible syntax, type inference, etc).  These features give Scala the “feel” of a dynamic scripting language like Ruby without really sacrificing the strengths of the compiler and static typing.  It seems that this illusion is so effective as to actually have fooled most people into lumping Scala in with the dynamic crowd.  While this is good in the sense that Scala is getting more attention this way, it also leads to a rising misconception of what Scala is and what it’s good for.

Ironically, many of the use-cases being presented in these language comparisons are best solved by a mid-level language like Java or Scala, rather than high-level languages such as Ruby, Python or Groovy.  This actually seems to be leading to even more trouble in the world of language choice as the problem domain itself becomes blurry.  Many developers these days are trying to apply Ruby to large desktop application development, or (just as bad) apply Java to simple CRUD and RAD applications.  It’s the old adage: if all you have is a hammer, every problem looks like a nail.  Developers are starting to select languages based on “what’s hot” rather than what offers the most effective solution.

The rampant number of Ruby-Groovy-Scala comparisons is not the problem, but rather a symptom of a greater problem: language overload.  Even confining our search to the JVM, we can find so many wonderful languages of every size, shape and type-system.  All of these languages have their creators pushing their product as the solution to life, the universe and everything.  With so much noise pervading the language arena, it’s easy to see why many developers get confused when performing that crucial selection of tools.

Unfortunately, there doesn’t seem to be a “nice and easy” solution to the problem of language selection noise.  Every language will always have its proponents, people who try to sell the language as the cure for life.  Getting them to just shut up and realize the imperfection of their product is just about as likely as convincing the masses not to listen to them.  So it seems that for the foreseeable future, we will continue to see rants, comparisons and rationales taking languages X, Y and Z out of context and attempting to come to a sane conclusion.

Techniques of Java UI Development

18
Feb
2008

Too often these days I see Java developers new to the psuedo-science of UI development finding themselves completely lost before they even get started.  There are a lot of misconceptions about the “best” and “easiest” way to create a professional UI in Java, and precious few resources which attempt to clear up the confusion.

In this article, I’m going to make use of the Swing framework simply because it’s more widely known.  All of the same techniques and processes apply to SWT with equal validity.  I’ll walk through the steps required to create a simple form UI in the easiest and most maintainable fashion.  I’m well aware that there are faster ways of doing this, but in my experience, this process will lead to more maintainable and less rigid UIs.

Note: These steps will obviously be different if you’re working in a team or with a designer.  The intention of this article is not to present the “be all, end all” of UI design practices, but rather to be a set of guidelines for the common-case developer.

Design

It may surprise you, but the very first step in creating a UI is design.  You have to know fairly precisely how you want the UI to look and (more importantly) behave.  This step is more than just drawing a few mockups in Illustrator.  This is sitting down and hashing out what you want the controls to do when the window is resized, if the state of any controls should be tied to that of others, whether your labels LEFT aligned or RIGHT, etc.  Often I find the easiest way to start is with a diagram:

 Diagram

This is what my diagram would look like, but it’s important to note that this isn’t a “one size fits all” style.  The point of the diagram is to give you a medium to lay out the semantics of a panel and to help you understand just what it is you have to do.  This step becomes invaluable later on as you start actually writing code.

The important thing about this diagram is that it is annotated.  Anyone can just draw a picture of what they want the UI to look like, what components to use, how they should be spaced.  The real meat of the question is not what does it look like, but how does it behave.  One of the biggest questions Java UI developer have to answer (as opposed to, say .NET developers) is how do the controls behave when the form is resized.  This is a much more complicated question than “where should the component be placed” or “how should it look”.  In essence, the question is asking “what sort of layout manager configuration do you need?”

There are two directions which need to be considered: vertical and horizontal resize.  This may not be news, but it’s something that a lot of developers (myself included) seem to forget.  The way the form scales horizontally is just as important as how it scales vertically, so it is critical that these considerations are dealt with in the design phase.  You’ll notice on my diagram that I have annotated almost every component with scaling constraints, dictating visually how the component will behave as the form changes size.  For example, all but one of the text fields scale out horizontally, but remain with a fixed height.  The one text field which does not scale horizontally is annotated so that it remains with completely fixed dimensions, both vertical and horizontal.  It’s also important to note that the City text field scales horizontally, “pushing” the State combo box as it does so.  Thus the state selection is always right aligned, the city label is left aligned, and the intervening space is filled with the city text field.  This sort of “resize mapping” (to coin a phrase) must be considered before any code is written.

You’ll notice that there is just a single annotation indicating behavior when the form resizes vertically.  This is because, like most form-based UIs, there isn’t really a good way to deal with this situation.  In this case, I just drew a line through the form where I thought I saw the most logical split and indicated annotatively that this “non-component” should be what scales vertically.  Thus the buttons remain fixed to the bottom, while the remainder of the components build down from above.  Some forms have text areas (multi-line text fields) which may scale vertically, filling this space.  Our form however has no components which logically scale in the vertical direction, thus we invent a non-component which fills the gap in our imagination.  This non-component will only be reflected in the layout constraints, we won’t have to add anything extra to the panel itself.

When drawing this diagram, note that I wasn’t afraid to make mistakes and then annotatively clarify them.  For example, I made the form to narrow to clearly express the intent of the layout.  The City and State fields take up more room than I expected.  Rather than starting from scratch, I just draw the fields as it seems most intuitive and then indicate with arrows that they should be aligned with the other text fields.  Likewise, the Line 1 and Line 2 fields should actually stretch all the way to the edge of the section.  This wasn’t clear in the diagram, so I added the –| annotation to remind myself of this fact.  Always remember that we’re not trying to make an unambiguous specification, just a reminder to ourselves.  It’s a design aid, nothing more.

The final bit of annotative goodness which needs to be observed is the “disables” marker attached to the Address checkbox and the section below it.  This marker indicates some basic UI state behavior: when the checkbox is selected, the section will be enabled; otherwise the section below (and all its components) will be disabled and no data may be entered.  Note that the annotation doesn’t say “clears” or “resets”, since we just want the section to be disabled and ignored.  Someone may disable the address section only to realize later (prior to saving) that they actually do need to enter an address.  They would likely want all of their previous changes to still be there, rather than completely lost.

Notice what we didn’t annotate here.  We didn’t clutter our design with intuitive concepts like “Save button saves the contents and closes the window”.  Rather, we left that up to our imagination and (likely) prodigious memory.  Remember that this diagram is for our use, not our manager or the other developers in our team.  If we were building a diagram for external consumption, we would have had to make an effort to be more clear.  As it is, we can trust ourselves to build a UI that makes sense according to convention.

First Steps

So we have our design diagram in hand, it’s finally time to consider some code.  It’s usually around this time that developers start thinking about what tools to use and how they can be of help.  Let me start out by saying flat-out that I am not in favor of any graphical UI design tool.  Don’t get me wrong, I think Matisse is a brilliant bit of engineering and a credit to the entire NetBeans team, but it’s not a silver bullet.  In fact, I don’t even think it’s a bullet worth firing.  I could create the code for the diagramed UI in a few minutes using Matisse, but what would the code look like?  Would it be code worth coding?

image

javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
  jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
  .addGroup(jPanel1Layout.createSequentialGroup()
    .addContainerGap()
    .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
      .addGroup(jPanel1Layout.createSequentialGroup()
        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(jLabel3)
          .addComponent(jLabel4)
          .addComponent(jLabel5))
        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
        .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
          .addComponent(jTextField4, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE)
          .addComponent(jTextField3, javax.swing.GroupLayout.DEFAULT_SIZE, 376, Short.MAX_VALUE)
          .addGroup(jPanel1Layout.createSequentialGroup()
            .addComponent(jTextField5, javax.swing.GroupLayout.DEFAULT_SIZE, 282, Short.MAX_VALUE)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jLabel6)
            .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
            .addComponent(jComboBox1, javax.swing.GroupLayout.PREFERRED_SIZE, 
                 javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
      .addGroup(jPanel1Layout.createSequentialGroup()
        .addComponent(jLabel7)
        .addGap(18, 18, 18)
        .addComponent(jTextField6, javax.swing.GroupLayout.PREFERRED_SIZE, 93, 
            javax.swing.GroupLayout.PREFERRED_SIZE)))
    .addContainerGap())
);

Oh yeah, I’m looking forward to working with that code.  What’s actually worse is you can’t work with that code.  NetBeans prevents you from editing the auto-generated sections of the code.  Meaning if Matisse got it wrong (and it did a couple times during the construction of the screenshot), there’s no way for you to fix it by hand.  And even assuming you drop back to your favorite external editor to make your fix, the next time you open the file in NetBeans, your changes will be overwritten.  Matisse is a totally dominating, uncompromising beast that takes over your UI and doesn’t let go.

Honestly, this isn’t a stab at NetBeans in any way.  Matisse does a fine job auto-generating its code and it’s amazingly good at inferring layout constraints based on convention and control placement.  It’s no substitute for fine-grained manual control however.  People may argue that “UI design is a visual task”, but that’s what the design diagram is for.  I’ve worked with some fairly complex Swing UIs in my time, and let me tell you that if you don’t know how to write the code by hand, you’ll never be able to maintain it.  And if you can’t maintain your UI code, that’s going to cause tons of problems down the road.  So no quick and easy tools for our UI-making.

If we’re going to write the code by hand (and we are), then we’re going to need to decide which layout manager(s) to make use of.  This is where the diagram can be extremely helpful.  Just looking at our design suggests a grid based approach, a subpanel (for the address) and a footer panel for the buttons (probably using something like FlowLayout).  Working from the high-level down to the fine-grained details:

  1. From top down: we have a body, some empty space which stretches on resize and a footer section.  This suggests the use of BorderLayout with the “body” as the CENTER constraint and our button panel as SOUTH
  2. Button panel appears to keep the components centered, so FlowLayout would be the most appropriate
  3. Body panel seems to be a grid of two columns, one for the labels and one for the text fields.  The Address component spans both grids with a left offset of 15-20 pixels.  Below this is the address panel which spans both columns and stretches to fill.  The address panel is not set to stretch vertically, but is rather attached to the top of its cell, allowing empty space to stretch below it
  4. Address panel appears to be a grid divided into four columns.  The first two rows have text fields which span three columns.  The bottom row contains a text field which also spans three columns, but does not stretch to fill

And no, I’m not concerned with resolution independence here.  After all, if Apple doesn’t care, why should I?  :-)

This process mainly depends upon familiarity with layout managers and some experience with form layout.  I happen to know that some of the most powerful layout managers are grid based, so I try to fit most UI designs into a grid.  I also know that Swing comes with a very nice (if limited) default layout manager called BorderLayout.  Many high-level layout constructions fall into a category of problems which are trivial to manage with BorderLayout (like our button footer).  There really are a number of patterns which can be kept in mind during this layout process, but in the end it all comes down to experience.  Once you’ve built a form-based UI, the steps involved in creating another become somewhat instinctive.

We’ve already specified two of the three layout managers we will need for this UI.  All that remains is to select a grid-style manager which can accommodate both fixed and fluid constraints.  We could use GridBagLayout, but for reasons which should be obvious, I like to avoid that one.  Unfortunately, Swing doesn’t really have another grid layout which is powerful enough to accomplish our goals.  So we look outside of the core Swing API.

There are a number of third-party layout managers available, but the one I really favor is a layout manager I ported from SWT a number of years ago.  SWT’s GridLayout is basically most of the power of GridBagLayout, wrapped in a clean and intuitive API.  It’s more than sufficient for our purposes, and it won’t needlessly gum up our code with dozens of verbose constraint definitions.

Fleshing it Out

With all three layout managers chosen and most of the constraints solidified, it’s time to get our hands dirty and actually write some code.  For simplicity of example, we’re just going to put everything into a single class with a main method.  This main method will simply set the look and feel (avoiding metal) and launch the frame:

public static void main(String... args) {
    EventQueue.invokeLater(new Runnable() {
        public void run() {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
            } catch (ClassNotFoundException e) {
            } catch (InstantiationException e) {
            } catch (IllegalAccessException e) {
            } catch (UnsupportedLookAndFeelException e) {
            }
 
            new ContactDetails().setVisible(true);
        }
    });
}

As per convention, all of our components will be created and initialized in the class constructor.  Some people are proponents of the init() or initComponents() convention, but I personally don’t see that it serves any purpose.  If you’re just calling the method from the constructor anyway, why bother?

You’ll notice that we’ve wrapped the entire launch sequence in an invokeLater()This is extremely important.  Swing (or rather, AWT) manages the entire event dispatch queue for you in separate threads.  It’s not like SWT where the event dispatch is manually, er…dispatched.  This means that the main thread is not in fact the EDT (event dispatch thread).  If we just made a call to our constructor within the main thread itself, we would be creating components and interacting with Swing in a thread other than the EDT.  Swing is pretty good at making sure that the event dispatch still works, but odd bugs can creep in around the edges.  Ever wonder why the setVisible(true) invocation sometimes blocks and doesn’t at other times?  This is usually due to the differences between an in-EDT invocation and a main thread call.  Romain Guy turned me on to this misunderstood bit of Swing usage, and I’ve been using it ever since.  Believe me when I say that you’ll save yourself a world of hurt by making sure you’re square with the EDT.

Now that we’re sure our invocations are properly wrapped, we can get started writing the UI layout code, adding the components to the frame.  Honestly, this code is more than slightly verbose and repetitious, so I’m not going to reproduce all of it.  This is just the interesting stuff:

setDefaultCloseOperation(EXIT_ON_CLOSE);
setSize(400, 265);
 
JPanel body = new JPanel(new SWTGridLayout(2, false));
getContentPane().add(body);
 
body.add(new JLabel("First Name:"));
 
JTextField firstName = new JTextField();
 
SWTGridData data = new SWTGridData();
data.grabExcessHorizontalSpace = true;
data.horizontalAlignment = SWTGridData.FILL;
body.add(firstName, data);
 
body.add(new JLabel("Last Name:"));
 
JTextField lastName = new JTextField();
 
data = new SWTGridData();
data.grabExcessHorizontalSpace = true;
data.horizontalAlignment = SWTGridData.FILL;
body.add(lastName, data);
 
JCheckBox address = new JCheckBox("Address");
 
data = new SWTGridData();
data.horizontalSpan = 2;
data.horizontalIndent = 15;
body.add(address, data);
 
JPanel addressPanel = new JPanel(new SWTGridLayout(4, false));
addressPanel.setBorder(BorderFactory.createEtchedBorder());
 
data = new SWTGridData();
data.grabExcessVerticalSpace = true;
data.horizontalAlignment = SWTGridData.FILL;
data.verticalAlignment = SWTGridData.BEGINNING;
data.horizontalSpan = 2;
body.add(addressPanel, data);

 image

Well, this isn’t quite like our diagram, but it’s close enough.  Remember that the diagram is only a guide.  It’s purpose is to focus your thought and allow you to create the layout constraints.  It’s never intended to be a hard-and-fast spec.

You’ll notice how much more readable the manually written code is than its Matisse-generated counterpart.  It may be repetitive and annoying, but it’s very readable, very maintainable.  We could actually add a little bit of abstraction here (e.g. createGridData()) to make things even more readable, but that’s overkill for this example.  Actually, not only is this code readable and maintainable by myself, but I can also check it into a shared repository and other people (potentially using different editors) can make changes to the layout and UI logic and expect them to actually work.  I’ve had nothing but bad experiences when using UI builders in a team setting, especially builders like Matisse which rely on external meta files.

One thing that is worth calling attention to is that magic number pair in the setSize() invocation.  Of course, I could have used pack(), but that rarely sizes a frame as something which looks pleasing to the eye.  I’ve found that the easiest way to get a frame which is sized nicely for most platforms is to put in some easy default width and height (say, 400×400), then once the layout is fixed, size the form by hand.  Literally run the form and resize the window until everything looks good.  Once that’s done, screenshot it and measure the width and height in Paint.  :-)  It may sound crude, but it works surprisingly well.  Of course, the size will have to change a bit, depending on which platform the app is running on, but this is easy enough to handle.  Usually I repeat the process on each platform, taking the largest width-height pair as making that the fixed standard for all platforms.

Conclusion

And there you have it, a simple Java form UI created with minimal fuss and maximum compatibility.  More importantly, the techniques presented in this article can be applied to more UIs in future, and not just forms.  All of us at some point have to build a basic Swing UI, and it’s best if we’re comfortable doing so and able to create something that’s worth keeping around.  Hopefully the concepts I have presented will be useful to you the next time you find yourself faced with a similar situation.