Showing posts with label DesignGridLayout. Show all posts
Showing posts with label DesignGridLayout. Show all posts

Friday, May 01, 2009

DesignGridLayout: where do we stand?

It's been a long time I haven't blogged on DesignGridLayout. Several events have concurred to this lack of communication:
  1. I have bought a brand new notebook (for those interested: that's a Sony VAIO Z)!
  2. It's taken me a long time to have DesignGridLayout tests run on both my old notebook (Windows XP) and my new one (Vista)
  3. I have plaid with Hudson on my old notebook to setup a continuous build for DesignGridLayout
Now that all that works (after a lot of pain for the second point, and thanks to the active help of Alex for quickly improving FEST "specially" for me), I can finally resume my work on the project.

Where do we stand?

Version 1.2, which I am currently working on, shall have the following features:
  1. alignment synchronization of multiple panels (all using DesignGridLayout): particularly useful to make smooth panels transitions in Tab panes and Wizards.
  2. optional consistent baselines spacing between consecutive rows (only for fixed height rows).
  3. addition of a new API to notify DesignGridLayout of special components behavior with regards to vertical resize.
For these features, I have already made a big refactoring of the source code.

Enhancement #2 above is already implemented in Subversion trunk, here are 2 screenshots comparing the same layout with and without consistent baseline spacing, respectively:

Note the consistent spacing between labels baselines for all rows but the second one (because it has variable height) and the last one (because it has an emptyRow() before it to visually separate that row from the previous one).

Without consistent baselines spacing, all vertical gaps conform to the values provided by the installed look & feel, but due to different heights of components and different vertical gaps, the inter-row spacing doesn't look consistent and is not very eye-friendly (this is particularly true when observing the column of labels).

Regarding synchronization of alignments between different panels (enhancement #1 above), the following screenshots help showing what works today (excerpts from DesignGridLayout automatic tests).

The example below is a dialog with a tabbed pane, embedding two different tabs. The first 2 screenshots show both tabs, built with DesignGridLayout, without synchronization.

Please note that there are no horizontal or vertical alignments between both tabs, this is particularly noticeable when running the application and changing from one tab to the other.

Now, let's add just one line of code to the layout building code, as follows:
Synchronizer.synchronize(layout1, layout2).alignGrids().alignRows();
Then the screenshots become different (note that I have put the 2nd tab both on the right side and on the bottom of the first tab so that you can easily check alignments in both directions):

You can notice the alignment of the origins of both columns of fields, and you can also check that all rows baselines from both tabs are aligned with one another.
This example is particularly interesting due to the use of Java -default- Metal look & feel, in which different kinds of components have different heights (compare the heights of JTextField and JComboBox); other look & feels may have consistent heights for all components, which would not show the difficulty of aligning each and every row from one tab to its matching row in the other tab. I am not sure other LayoutManagers can do that, I haven't checked yet though.

Back to DesignGridLayout V1.2 schedule, I still have some work with:
  • baseline synchronization
  • new API for vertical resize behavior customization (point #3 above)
  • layout calculation optimization (avoid multiple recalculation)
For DesignGridLayout 1.2, I plan to release a beta with new features and the new API so that I have time to polish it before a release candidate. Here is the expected schedule:
  • End May: 1.2 beta 1 out, beta testing will last one month
  • End June: 1.2 rc 1 out, followed by final release after 2 weeks (or 2 weeks after latest rc if bugs are reported)
What's next?

Later, for V1.3, I foresee enhancements to non-grid rows API & functionality:
  • possibility to separate 2 components with unrelated gaps
  • smart positioning of standard buttons (OK, Cancel...) based on the runtime platform
  • make component sizes consistent across several rows (currently, sizes are consistent inside individual rows only)
It's hard to predict what a release date could be, but since I don't see too much complexity in these features (but as usual, the difficulty will be in finding the right API for them), I think it should be possible to have a first release candidate less than 2 months after final release of V1.2, around September-October.

Sunday, February 15, 2009

Announce: DesignGridLayout 1.1 released!

Two weeks after the fourth release candidate of DesignGridLayout 1.1 (1.1-rc4), I have decided to release the official final 1.1 release.

Compared with previous official 1.0 release, this version brings the multiple rows span components feature, in addition to a few enhancement and bugs fixes:
  • fixed several problems with baseline alignment (only on Java 5): issues #3 and #27
  • fixed a problem with smart vertical resize of JList (issue #28)
  • fixed an exception with multi-grid feature (issue #26)
  • fixed an exception when creating a row but adding no component to it (issue #30)
  • fixed a bad look with grids having just a label but no component (issue #31)
  • optimized the size of the example jar (removed all screenshots that are used during automatic tests)
  • completely refactored the examples application
  • added an option to disable "smart vertical resize" feature (issue #34)
Now I can go back to work on next release (1.2). Future version 1.2 will have two major features:
  1. Synchronization of several layouts: with this feature it will be possible to ensure correct alignments (vertical or horizontal) across several panels using DesignGridLayout. This will be particularly useful for use inside JTabbedPanes or in Wizard dialogs.
  2. Extension API to customize policies related to components vertical resize (the current -internal- policy recognizes only vertical JSliders and JScrollPanes as variable height components, but some users have expressed the need to recognize also other specialized components)
First enhancement is on the way but will take some more time since it is a quite complex feature. I intend to release a 1.2-beta with this feature only, for users to check if it fits their needs well in synchronizing alignments. 1.2-beta is expected by mid-March if I can progress well.

If you're not afraid of using a "work-in-progress" version, you can get it from Subversion trunk, build it according to the instructions on the web site, then take a look at the new Synchronizer class.

I hope to have a first 1.2 release candidate by end of March or early April.

Saturday, February 07, 2009

JInternalFrame lesson learned

I am not a big fan of MDI (Multiple Document Interface) UI in general. I much prefer SDI or, better, UIs with docking capability.

Hence I don't have much experience with Swing JDesktopPane & JInternalFrame (the basic building bricks for creating an MDI UI in Swing).

However, a few days ago, an engineer from my company, developing a sample MDI UI (for an exercise related to a "GUI training"), was using DesignGridLayout LayoutManager and found a strange behavior in one of his windows, as demonstrated in the following screenshot, taken after a few pixels increase of the frame height (note the table overlapping the "Open" button):


The layout code is there:
UIHelper.addGroup(layout, "Criteria");
layout.row().grid(projectNameLabel).add(projectName).grid(projectNumberLabel).add(projectNumber);
layout.row().grid(workloadMinLabel).add(workloadMin).grid(workloadMaxLabel).add(workloadMax);
layout.row().grid(startDateFromLabel).add(startDateFrom).grid(startDateToLabel).add(startDateTo);
layout.row().center().add(searchButton, resetButton);

UIHelper.addGroup(layout, "Results");
layout.row().center().fill().add(projectScrollPane);
layout.row().center().add(openButton);
I found it strange because I had never faced this bug with DesignGridLayout before. Hence I have started investigating. The first surprise to me was that I could not reproduce this problem in a JFrame or a JDialog!

Hence I've written the simplest Test Case application, using JInternalFrame, to reproduce this problem:
JFrame mainFrame = new JFrame();
mainFrame.setName(getClass().getSimpleName());
mainFrame.setBounds(30, 30, 800, 600);
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

JDesktopPane desktop = new JDesktopPane();
mainFrame.setContentPane(desktop);
mainFrame.setVisible(true);

JInternalFrame frame = new JInternalFrame("", true, true, true, true);
frame.setLocation(30, 30);

JPanel top = new JPanel();
JTable table = new JTable(CONTENTS_PLAYERS, COLUMNS_PLAYERS);
setTableHeight(table, 3);
DesignGridLayout layout = new DesignGridLayout(top);
layout.row().center().fill().add(new JScrollPane(table));
layout.row().center().add(new JButton("OK"));

frame.setContentPane(top);
frame.pack();
desktop.add(frame);
frame.setVisible(true);
try
{
frame.setSelected(true);
}
catch (java.beans.PropertyVetoException e)
{
}
Here is the screenshot (at preferred size):

After a long debugging session, I found out that the preferred height of the JScrollPane in the above example was incorrect at the time Swing calls LayoutManager.preferredSize(); but when the frame is displayed (LayoutManager.layoutContainer() is called), the height of the JScrollPane is correct!

Further investigation (and pixels counting on the screen!) has shown me that during the call to pack(), the preferred height of the JScrollPane is equal to the preferred height of the embedded JTable, plus the JScrollPane borders, but it does not include the JTableHeader!

After checking Swing source code and trying to change slightly the code that packs and shows the JInternalFrame, I found out that the problem is that pack() is called too early!

The following change just removed the problem:
// Order of calling desktop.add() is important wrt frame.pack() call!
desktop.add(frame);
frame.pack();
//desktop.add(frame);
frame.setVisible(true);

The rant

This problem is specified nowhere in JDesktop or JInternalFrame javadoc! In addition, the JInternalFrame section of the Swing tutorial is totally unclear and even misleading about that point!

Well that's a lesson learned for me:
Always make sure you add JInternalFrame to the JDesktopPane before calling pack().

Event better: avoid MDI like the plague (I remember, a long time ago, being allergic to Swing MDI because of various other problems...)

Wednesday, January 07, 2009

Announce: DesignGridLayout 1.1-rc1 released!

After one month of heavy work, I am proud to announce the first release candidate of DesignGridLayout 1.1.

This version brings one major new feature and fixes a few bugs:
  • new support for components to span several rows (RFE #10)
  • fixed problems with baseline alignment in JRE5 (issues #3 and #27)
  • fixed a problem with smart vertical resize of JList (issue #28)
  • fixed a potential exception that could occur in very specific layouts (issue #26)
In addition, the examples demo application has been completely rewritten in order to show all DesignGridLayout features along with description and source code. This application can now constitute a very effective way to learn DesignGridLayout from scratch in no time. It is also useful to current DesignGridLayout users who want to learn new features.

The new support for components spanning multiple rows allows you to define layouts that look like this:


The source code for that is quite straightforward:
layout.row().grid(label1).add(field1).grid(label2).add(list);
layout.row().grid(label3).add(field3).grid().spanRow();
layout.row().center().add(button);
It is important to notice that "smart vertical resize", one of DesignGridLayout unique features, is still active on components spanning multiple rows. You can see on the following screenshots the same layout as above during vertical resize (note the list always show only entire rows and never truncates any row):





Of course, you can also see this behavior "live" if you launch the examples application!

I consider this release candidate to be ready for production and, if no bugs are reported, I expect a final release in less than one month.

Enjoy!

Thursday, January 01, 2009

DesignGridLayout: row-span support soon ready!

December 2008 has been a busy month for me on DesignGridLayout.

Although not yet completely ready for a first 1.1 release candidate, the current Subversion trunk already includes some interesting changes:
  • added support for components spanning multiple rows (issue #10): this is further discussed below
  • fixed several problems with baseline alignment (only on Java 5): issues #3 and #27
  • fixed a problem with smart vertical resize of JList (issue #28)
  • fixed an exception with multi-grid feature (issue #26)
  • optimized the size of the example jar (removed all screenshots that are used during automatic tests)
  • completely refactored the examples application: see below

New showcase application

Much of my past efforts on DesignGridLayout were spent on reworking the original examples application which was overly simplistic and not a very good way to "sell" DesignGridLayout. Hence I have written from scratch a new showdown application that:
  • demonstrates all DesignGridLayout features from the basics to the more advanced uses (arranged in a tutorial-oriented way)
  • describes each feature
  • always shows the relevant source code that produces every sample
  • allows you to launch the layout in a separate frame in order to test its resize behavior
  • asks you to choose your preferred Look & Feel so that you are not limited to the old-fashioned default Metal look & feel
Here is a screenshot of this new showdown application (with Windows XP look & feel):


Now the good news is that, although there is no official release of this new DesignGridLayout application, you can already launch it today! Just click the jnlp link below:


Multiple row span

This is the main improvement to the future DesignGridLayout 1.1.
I had to slightly change the API (completely backward compatible with 1.0), as described in one previous post on this blog. As a reminder, here is a simple example:
layout.row().grid(label("lbl11")).add(field("field11")).grid(label("lbl12")).add(list());
layout.row().grid(label("lbl21")).add(field("field21")).grid().spanRow();
layout.row().center().add(button());

Note the spanRow() call on the second line of code. This states that "in this position of the current row, span the component that is in the same position on the row above".

The result looks like this:


Please note there's a catch in this snapshot: the list size does not obey the "smart vertical resize" feature of DesignGridLayout (first introduced here). That's one open point in the current source code.

This open point is to decide whether the layout should keep the "smart height" for a multi row span component or not:
  • if we don't keep it, then at preferred size, the bottom border of the list is aligned with the bottom border of the field (example above)
  • if we keep it, then there won't be such alignment, which may look quite ugly as in the snapshot below


I am currently inferring about what to do here. I wonder if this should be left as an option (new API) to the end user. Among the available options:
  1. disable smart vertical size for multi row span components (current situation)
  2. enable smart height with loss of bottom border alignment (as in snapshot above)
  3. enable smart height but keep bottom border alignment (or baseline alignment with the last visible row of the list). This means that there will be a strange vertical spacing between first and second field. I am not even sure that this is feasible actually, I'll need to investigate further.
Additional decisions would concern the form of the API to select the behavior, in particular, I have to find out a light API (that does not make the current one heavier) and which granularity to give this API, more precisely, at which level the behavior should be selectable: the whole layout, each row, or every single multi row span component? The finer the level, the heavier the API, the more complex it is to use...

The behavior of spanRow() is described in the future 1.1 javadoc (not released, but you can already build it from the Subversion trunk).

A second catch (not visible in the example above) with the "spanRow()" API is that, unfortunately, it is now possible to code (and compile) a layout that cannot be rendered at runtime, because it just makes no sense, as in the following example:
// spanrow() called on a subgrid with different number of columns
layout.row().grid(label("lbl1")).add(field("field1"), field("field2"), field("field3"));
layout.row().grid(label("lbl4")).add(field("field4")).spanRow();

The spanRow() call on the second row does not match a single component of the first row, but there are 2 candidate components to be spanned: field2 and field3.

Since there is no way to discover this problem at compile-time, DesignGridLayout performs the check at runtime and will replace each incorrect spanRow() call with a special "marker" component (with a tooltip that gives further information) to show that the original source code has to be reworked.

The following screenshot shows many examples of incorrect API usage:


This screen is also available from the showdown application.

Roadmap

Once I can progress on the open point presented above, I will be able to propose a first release candidate of version 1.1. That should be ready by mid-january hopefully. The final 1.1 version will be released 3-4 weeks after the latest stable (no open bug) release candidate.

Enjoy!

Sunday, December 07, 2008

Announce: DesignGridLayout 1.0 released!

One month after the third release candidate of DesignGridLayout 1.0 (1.0-rc3), no new bugs being received, I have decided to release the official 1.0 version of DesignGridLayout.

I trust this version to be stable and bug-free (however, if you do encounter a bug with it, do not hesitate to report it, I will be glad to provide a fix in a timely manner).

For me, this means I can now start seriously working on the next enhancement (for 1.1 version) that will allow users to define some components spanning several rows. You can already have an overview of the future API (up to the current state of my reflection) if you are interested.

If you have further questions about DesignGridLayout, please don't hesitate to ask in the corresponding project lists.

Wednesday, November 05, 2008

Announce: DesignGridLayout 1.0-rc1 released!

I am particulaly glad to announce the first release candidate of DesignGridLayout V1.0.

DesignGridLayout is a Swing LayoutManager, revolutionary by its API, simple but powerful. Its main advantages are:
  • Good looking forms (alignment, spacing, sizing, visual balance): this is entirely taken over by DesignGridLayout itself without any special hint from the developer
  • Reduced learning curve for developers, thanks to its fluent API which is simple, effective, compile-safe and IDE code-completion friendly
  • No graphics designer needed: the API is the graphical designer
  • Readability and maintainability: you can literally "visualize" the layout by browsing the code that sets it up; inserting a new row of components is done by simply inserting a new line of code in the layout setup code...
  • Free: the project is open source and released under Apache License 2.0
Version 1.0-rc1 is available here or through the java net maven 2 repository (for more info, you can check my previous post and replace "0.9" with "1.0-rc1").
This version brings the following improvements:
  • #13: support for multiple groups of fields, each with its own label column
  • #5: smart vertical resize: DesignGridLayout automatically determines which rows should grow vertically and also make sure that components height is suitable to display an entire line of information (useful for JList, JTable, JTextArea)
  • #18: smarter horizontal resize behavior: now DesignGridLayout won't resize components under their minimum size
  • #9: automatic support of right-to-left text orientation based on Locale
  • #16: smarter gaps for empty rows
  • #15: resolution independence
  • #20: fixed ugly layout problem when container has a border
  • #12: now setLayout() is automatically called by DesignGridLayout constructor
Please note that V1.0 required API changes that I could unfortunately not keep compatible with previous 0.9 release. This should be the last time that happens (V1.1 should only extend the current API).

I consider the current version ready for production as the current test suite of DesignGridLayout is quite comprehensive and covers all its features.
However, I have decided to prepare a release candidate to give myself a chance to fix any problems that users may find but that I could not discover by myself (in particular, problems related to platforms that I don't have: MacOS-X, Linux, Solaris).

If needed, I will create further release candidates. I will wait about one month after a rc until I cut a final release.

What's next?
  • first of all, I'll get some rest;-)
  • then I'll spend some weeks on my other open source project, guice-gui
  • finally I'll start working on DesignGridLayout V1.1, which should include just one improvement (issue #10: support for components spanning several rows) which should be released as a Christmas present;-)
Enjoy it and don't hesitate to report any problems or enhancements!

Monday, November 03, 2008

In search of the perfect API (final part)

This is the last post of my series on DesignGridLayout API evolutions. As mentioned before, although I use DesignGridLayout to illustrate it, this series might be useful to any developer of libraries.

In the first, second, third and fourth posts, I have described the evolution of DesignGridLayout API from its original release to the latest -soon released now- V1.0.

This last post is more a reflection on the next probable evolution of this API. This means that anything shown in this post has not been implemented (and might be difficult to implement), but thinking in terms of API first rather than implementation should be your mantra when writing a library with a large users base (I'm dreaming aloud;-))

Immediately after releasing V1.0 (in a few days), I'll start working on V1.1, which main enhancement will be issue #10 (add support for components spanning several rows).

This is not an easy problem, API-wise; indeed, you have to keep in mind the following principles that DesignGridLayout project adheres to:
  • the feature (API) must be easy to understand and use
  • the code visually represents the actual layout
  • developer mistakes must be avoided or can be found (and eradicated) at compile-time
I would also add an important point here:
  • don't break the current API (and the existing code that uses DesignGridLayout library V1.0)!
With this in mind, let's try to sketch what we would like the user to write (in terms of code), starting from a layout that is possible in V1.0:
layout.row().grid(lbl1).add(table1).add(field2);
layout.row().grid() .empty() .add(field3);
layout.row().grid(lbl4).add(field4);
Note that table1 represents a JList in a JScrollPane with a preferred size of a few rows (let's say 2 for this example).

Here is a snapshot of the displayed layout for this snippet:


Here we can see that with DesignGridLayout V1.0, table1 is displayed at its preferred height but this fixes the height of its whole row. Hence a lot of empty space is lost on the second column of the layout. Instead, wee would like to have table1 span on the second row as well (that's why we have already put an empty() column in the same column in row 2).

Now how can we let the user indicate that table1 should span on 2nd row?

One option I have in mind is something like this:
layout.row().grid(lbl1).add(table1).add(field2);
layout.row().grid() .spanRow() .add(field3);
layout.row().grid(lbl4).add(field4);
Using "spanRow()" on the second row would have the following meaning:
  1. the component on the previous row at the same column position in the same sub-grid will span this row
  2. the horizontal span of this component will be the same as the column span defined for that same component on the previous row
  3. the sub-grid in which spanRow() appears will have its gridspan forced to the same value as the sub-grid in the same position of the previous row
  4. if the matching component in the previous row is empty() or empty(n), then spanRow() is also empty() or empty(n)
  5. if this is the first row in the layout, then spanRow() is equivalent to empty()
This definition solves some problematic cases when using column spans in a grid:
layout.row().grid(lbl1).add(table1, 2).add(field2);
layout.row().grid() .spanRow() .add(field3);
layout.row().grid(lbl4).add(field4);
Or even when using multiple sub-grids:
layout.row().grid(lbl1, 2).add(table1).add(field2);
layout.row().grid(1) .spanRow().add(field3);
layout.row().grid(lbl4) .add(field4) .grid(lbl5).add(field5);
However, there is a problem in this latter example: in second row, we specify a gridspan of 1, but the actual gridspan will be 2 (the same as in the first row)! This looks counter-intuitive.

One approach to solving this problem would be to prevent the possible call to grid(int gridspan) or grid(JLabel label, int gridspan).

But the only way to prevent it would be to explicitly indicate that, when we create a new sub-grid in a row, this row will include row-spanning components, and in this case we can restrict the interface to prevent setting explicitly gridspans.

For this we could simply change the signature of grid methods in ISubGridStarter interface:
public interface ISubGridStarter {
public ISpannableGridRow grid(JLabel);
public ISpannableGridRow grid();
public IGridRow grid(JLabel, int gridspan);
public IGridRow grid(int gridspan);
}
Here we introduce a new interface ISpannableGridRow which is defined as follows:
public interface ISpannableGridRow extends IGridRow {
public ISpannableGridRow spanRow();
}
This is just an extension of the normal IGridRow with one extra method that allows spanning of components.

Now there is another problem (unsolved yet, else this issue would be fixed in V1.0 already;-)), related to vertical resize feature.
DesignGridLayout has the notion of rows that can vary in height or not (based on the components they hold); in addition, it allows you to define a vertical grow weight factor for those rows with variable height (see DesignGridLayout.row(double weight) method).
Now that components can possibly span several rows, how should we address the way individual rows will be shared vertical space?
Or is this really a problem? Can't the current algorithm work as well? Only a prototype for issue #10 will tell us...

For the definitive API of DesignGridLayout V1.1, well, I guess we'll have to wait until official release;-) maybe as a Christmas present, or sooner if I can manage!

This post closes my long discussion on searching the best possible API for a library. Maybe, in a later post, I'll show implementation details for this API (in particular, how return co-variance (Java 5+) has helped me much for that).

Hope you found this series interesting. Don't hesitate to comment! Enjoy.

Sunday, November 02, 2008

In search of the perfect API (part 4)

This is the fourth post of my series about how the API of DesignGridLayout library has been improved. This series can be useful for anyone developing libraries (DesignGridLayout just illustrates improvements).

The first post discussed the original V0.1 API and its related issues.

The second post has shown how to solve most V0.1 issues.

The third post has dealt with API improvements driven by new features. In particular, one interesting change to the API has been to introduce a new interface ISubGridStarter to reduce the list of available methods in a context, these methods then returning the "full" interface IGridRow.

In this installment, I will talk about the latest changes made to the API before V1.0 is released (which should happen very soon now).

Latest 1.0-SNAPSHOT: back to 0.1?

First of all, I was not very happy with the new methods I introduced in DesignGridLayout class in one of both previous snapshots:
public class DesignGridLayout {
public INonGridRow leftRow();
public INonGridRow leftRow(double weight);
public INonGridRow rightRow();
public INonGridRow rightRow(double weight);
public INonGridRow centerRow();
public INonGridRow centerRow(double weight);
public IGridRow row();
public IGridRow row(double weight);
}
There is a lot of repeat here. In addition, it is not clear why "row()" is not "gridRow()" (lack of consistency).
In addition, the ISubGridStarter interface uses methods all named "label()" to actually do 2 things:
  1. Start a new canonical sub-grid
  2. Set a label to this new sub-grid (or no label at all)
So the name is not representative of the method behavior. In particular, it is extremely weird to call label() with no argument to indicate we start a new sub-grid with no label!

Actually, I prefer something like that (in terms of usage):
layout.row().left().add(...);
layout.row(1.0).grid(lbl1).add(...).grid(lbl2).add(...);
layout.row(0.5).grid() .add(...).grid(lbl3).add(...);
Several changes have occurred to make this kind of code possible.

First I introduced (once again) a new interface:
public interface IRowCreator extends ISubGridStarter {
public INonGridRow left();
public INonGridRow right();
public INonGridRow center();
}
I also modified ISubGridStarter (only the method names):
public interface ISubGridStarter {
public IGridRow gridJLabel);
public IGridRow grid();
}
You have to remember that IGridRow extends ISubGridStarter (which allows to later start new sub-grids in a row).

Finally, I simplified DesignGridLayout API:
public class DesignGridLayout implements LayoutManager {
...
public IRowCreator row();
public IRowCreator row(double weight);
public void emptyRow();
}
Now we have only 2 general methods to create a new row (whatever its type), one allows you to specify the vertical growth weight factor (the no-arg row() will automatically determine if the actual row should have variable height and, if so, assign it a default weight of 1.0).

Also note that we still keep emptyRow(), but it has lost its "int height" argument, this is due to an improvement of the behavior (issue #16: make emptyRow smarter).

Now we can code our layouts like that:
layout.row().grid(lbl1).add(field1)             .grid(lbl2) .add(field2).add(field3);
layout.row().grid() .add(field4).add(field5) .grid(lbl3) .add(field6);
layout.row().grid() .grid(lbl4) .add(field7).add(field8).add(field9);
layout.emptyRow();
layout.row().center().add(ok).add(cancel);
The snippet above will exhibit the following layout:


The source code reflects the way the container will be laid out.
Code completion is helpful in that it does not authorize useless, or erroneous, method calls.

In fact, we have just built a simple "DSL" for laying out a Swing container!

To summarize, here is what we have gained in consistency (compared with previous 1.0-SNAPSHOT):
  • you add a new row to your form with one of two methods, named the same (row())
  • once a new row has been created, you can only choose its type in a clear way (left(), right(), center() or grid())
  • on a grid row, you can start a new sub-grid at any time with grid(), this is consistent with the way you started the grid row (because a grid row starts with a sub-grid)
In addition to previous improvements:
  • it is never possible for users to call a meaningless API (e.g. add(JComponent child, int span) in a non-grid row)
  • thus the number of options (callable methods) to the user in a context is restricted to its minimum (easing the search of methods through code completion in your IDE)
Please note that throughout this series, the API was simplified a little bit (there are a few more methods to some interfaces, but not many) in order to make it easier to follow.

This API should now be considered stabilized and there should be no change until the first official release candidate 1.0-rc1 of DesignGridLayout (which should be in a few days now).

In the next (and last) installment, I will talk about the future extensions of DesignGridLayout API (due for V1.1), necessary to implement the issue #10 (Support for components spanning several rows).

Saturday, November 01, 2008

In search of the perfect API (part 3)

This is the third installment of the short series discussing how to enhance a library API to make it better for its users. The series is based on DesignGridLayout as a practical example.

The first post discussed the original API of DesignGridLayout V0.1, focusing on its weak points for the end user.

The second post introduced the API of V0.9, showing how most of V0.1 pain points were solved (in particular by using interfaces and making public only what has to be public).

This third post will describe two evolutions (performed in two distinct steps) made in the current development trunk (which I call 1.0-SNAPSHOT), originated in new features requested by some users.

First evolution to 1.0-SNAPSHOT

When implementing issue #5 (vertical resize behavior), it had to be possible to add a "vertical grow weight" factor to any kind of row (except empty rows), in the same manner as you can assign "weighty" in Swing GridBagLayout.

For that, I only modified DesignGridLayout class:
public class DesignGridLayout {
...
public INonGridRow leftRow();
public INonGridRow leftRow(double weight);
public INonGridRow rightRow();
public INonGridRow rightRow(double weight);
public INonGridRow centerRow();
public INonGridRow centerRow(double weight);
public IGridRow row();
public IGridRow row(double weight);
public void emptyRow(int height);
}
That was really straightforward and it worked well for the library user. Note that we still did not solve the API issue (existing from V0.1) allowing multiple calls to label() on grid rows, but where only one call is effective.

Second evolution of 1.0-SNAPSHOT


Issue #13 (grids with multiple labels) was an important request from DesignGridLayout users so I had first to find the right API before implementing this feature.

The first idea that came to my mind was an API that would allow the following snippet:
layout.row().label(lbl1).add(field1).label(lbl2).add(field2);
layout.row() .add(field3).label(lbl4).add(field4);
So I need multiple calls to label(JLabel) (which was easy because the current API already enabled that, that was even its main remaining flaw).

Please note the absence of a label at the beginning of the second row, which means that there will be no label at this position in the displayed form (however empty space will be added, so that field1 and field3 are vertically aligned with each other). But this possibility required to have the same possibility for the second (and next) label in a row, I needed something like that:
layout.row().label(lbl1).add(field1).label(lbl2).add(field2);
layout.row() .add(field3).label() .add(field4);
Note the new no-arg label() method. Here is the new IGridRow interface (excerpt):
public interface IGridRow {
...
IGridRow label(JLabel);
IGridRow label();
}
Adding this method solves the problem but leads to inconsistency in the API, that we see in the snippet above, you now have two ways to obtain the same result:
layout.row().label(lbl1).add(field1).label(lbl2).add(field2);
layout.row() .add(field3).label() .add(field4);
or
layout.row().label(lbl1).add(field1).label(lbl2).add(field2);
layout.row().label() .add(field3).label() .add(field4);
The second way should actually be the only way to get this result! There are two reasons why I want that:
  1. API Consistency (as mentioned before)
  2. It allows unclear code when creating specific layouts where one would want to completely skip the first label AND the first field (and define only the second label and field)
For example, what layout would be produced by the following snippet:
layout.row().label(lbl1).add(field1).label(lbl2).add(field2);
layout.row().label(lbl4).add(field4);
Should lbl4 belong to the first label column or the second one? If we want to avoid such inconsistencies we have to make it mandatory to call one of both label() methods when starting a grid row.

Concretely this requires adding a new interface returned by DesignGridLayout#row():
public class DesignGridLayout {
...
public ISubGridStarter row();
public ISubGridStarter row(double weight);
}

public interface ISubGridStarter {
public IGridRow label(JLabel label);
public IGridRow label();
}
I also refactored IGridRow interface to extend ISubGridStarter:
public interface IGridRow extends ISubGridStarter {
...
}
This way, we have one unique, consistent way to define our layout.

In next installment, I will explain some last minute changes in the 1.0-SNAPSHOT API, in order to make it definitely better (and easier to use).

Friday, October 31, 2008

In search of the perfect API (part 2)

This is the second installment of a sort series about the evolution of the API of DesignGridLayout library.
Although DesignGridLayout is used as an example, this series is more general and its goal is to show how you can improve the API of any library, and what pitfalls you should avoid.

In the first installment of this short series, we have visited the initial API of DesignGridLayout (version 0.1) and seen several drawbacks it exhibited.

Release 0.9 (heavy refactoring of both API and implementation)

When I took over the DesignGridLayout project, a few months ago, I had decided to completely refactor the library source code and make the API has good as possible; in particular, I wanted to remove all drawbacks of version 0.1.

First of all, I have added interfaces in order to isolate API from the implementation details (too many public classes and methods were exposed in version 0.1).

Since, in DesignGridLayout, the accessible features are different if you use:
  • a grid row
  • a non-grid row (center, left or right)
  • an empty row (which sole purpose is to provide some empty vertical space)
I have first decided to make different methods in DesignGridLayout class to create these various kinds of rows; one of my goals was to be able to provide a specific API according to each kind of row; in addition, deciding the type of the row at the time you create it from DesignGridLayout would remove another problem of V0.1 related to multiple changes of the row type (which for the end user is non-sense).
public class DesignGridLayout {
...
public INonGridRow leftRow();
public INonGridRow rightRow();
public INonGridRow centerRow();
public IGridRow row();
public void emptyRow(int height);
}
All names starting with I are interfaces (I borrowed this convention from .NET, although arguable at the beginning, I found it quite interesting in my situation).

Here are the interfaces (simplified, I put only the main methods for clarity):
public interface INonGridRow {
public INonGridRow add(JComponent... children);
}

public interface IGridRow {
public IGridRow label(JLabel label);
public IGridRow add(JComponent... children);
public IGridRow add(JComponent child, int span);
public IGridRow empty();
public IGridRow empty(int span);
}
Now we clearly set the list of methods available to each kind of row, excluding any unsupported method (which we didn't do in V0.1).

We have also replaced the constant EMPTY (previously defined in Row) and replaced it by 2 empty() methods that are more explicit and help the developer (when using IDE code completion facility).

With this API, we have solved most problems existing with V0.1 with one exception: one can still call label() several times in a grid row although it is not supported by the library.

Now the example code shown in the first installment can be rewritten
layout.row().label(lbl1).add(field1).add(field2).add(field3);
layout.row().label(lbl2).add(field4).add(field5);
layout.row() .add(field6).add(field7);
layout.emptyRow(18);
layout.centerRow().add(ok).add(cancel);
to be compared with the previous snippet:
layout.row().label(lbl1).add(field1).add(field2).add(field3);
layout.row().label(lbl2).add(field4).add(field5);
layout.row() .add(field6).add(field7);
layout.row().height(18);
layout.row().center().add(ok).add(cancel);
The difference may not be remarkable here, but believe me you will see the difference when using your preferred IDE (code completion will not show you a plethora of irrelevant methods for the current context).

That's it for the API of V0.9. In next installment, I will describe 2 evolutions (due to adding new features) made to the API in 2 snapshots of the future V1.0.

That's it for this time. Enjoy!

Thursday, October 30, 2008

DesignGridLayout: in search of the perfect API (part 1)

This is the first installment of a short series (4 or 5 posts) telling the story of DesignGridLayout API.

From the first beginning, DesignGridLayout was meant to be easy to use in order to build Swing forms; its API was meant to make the use of any graphical designer totally useless. It was also meant to give code maintainers the possibility to "see" the UI while only browsing the source code.

For that, it has used a fluent API (some may use the term "DSL" or "Domain Specific Language" in this context) so that you could, in one line of code, define one row of your UI, eg:
layout.row().label(lbl1).add(field1).add(field2).add(field3);
layout.row().label(lbl2).add(field4).add(field5);
layout.row() .add(field6).add(field7);
layout.row().height(18);
layout.row().center().add(ok).add(cancel);

The code above would give the following result in a dialog:


As mentioned above, this series is about the evolution of that API. Prior to giving details on API history and changes motivation, I must thank Joshua Bloch for his excellent book "Effective Java, 2nd Edition" (I read it this summer, I had never read the first edition before) which I recommend to every Java programmer and particularly to library developers; this book has helped me a lot in thinking about providing a better API to DesignGridLayout users.

Initial release 0.1

Version 0.1 could be seen as a good prototype of implementing canonical grids in a Swing layout with a rather good API.

The library was then made of 4 classes only (all public)!

public class DesignGridLayout implements LayoutManager {
...
public Row row() {...}
}

public class Row {
public static final JComponent EMPTY = ...;
public Row label(JLabel label) {...}
public Row add(JComponent child) {...}
public Row add(JComponent... children) {...}
public Row add(JComponent child, int span) {...}
public Row left() {...}
public Row center() {...}
public Row right() {...}
public Row grid() {...}
public Row height(int height) {...}
...
}

public enum RowAlign {...}

public class RowItem {...}

Quite straightforward, wasn't it? However, this API suffered from several problems (don't forget that was a kind of prototype at that time):
  1. Some public classes (RowItem, RowAlign) should have been hidden because they were implementation details. By exposing these classes, you make them part of the API, preventing potential future changes in the implementation (don't forget that when designing an API, you should strive to make it as good as can be, but also as narrow as possible, because it will be hard to change afterwards -without breaking compatibility).
  2. Many public methods from DesignGridLayout and Row classes should have been hidden because they served no purpose to the user of the library, once again they were implementation details that were unintentionally added to the API, leading to a crippled API (in the sense that all those useless methods appear and are proposed by IDE code-completion feature)
  3. This API made it possible to change the type of a row as many times as you want (by calling e.g. layout.row().grid().left().right().center().add(...);)
  4. You could add a label to a non-grid row (which was not supported by the implementation) so this could give wrong impressions to users of the library
  5. You could add a component with a column span > 1 in a non-grid row (not supported)
  6. You could specify the height of any row (which is not advised for a proper look). Usage of the height(int) method was intended as in layout.row().height(18) (no component added to the row).
To summarize the main issues with this API:
  • many methods or classes should not be public (they should be either private or package-private)
  • the API allows code that is not supported at runtime and may give the code writer/reader a false idea of how the UI looks like
In the next installment, I will talk about the API changes in version 0.9 (current official version of DesignGridLayout).

That's all for today!

Sunday, October 26, 2008

DesignGridLayout: final 1.0 soon released! Or not?

Today, I have finished fixing the last of the high priority issues of DesignGridLayout.
All fixes are already available on Subversion trunk, and also as snapshots there (note: it is not available through maven).

The past 3 weeks were quite tough in terms of issues fixing, with a lot of refactoring, improvements, and clean-up.

Now I am quite happy with the current standpoint about new features:
  • "smart vertical resize" (already discussed in this post)
  • automatic support of right-to-left orientation Locales (discussed there)
  • smarter -and simpler- API for empty rows (no need to specify a number of pixels: that's one step toward resolution independence)
  • better minimum size management (previously, DesignGridLayout would calculate a minimum size equal to the preferred size of the Container, but during very narrow resize, it would shrink each component to ridiculously small widths)
  • automatic call to setLayout() from DesignGridLayout constructor
  • last, but not least, smart support for forms with multiple label columns (this required some extension of the "spirit" of canonical grids, rising to the concept of "canonical sub-grids")
The concept of "multi-label grids" (or "canonical sub-grids" as I often name them) still needs to be introduced on the project web site (or on this blog) but that should not be a very heavy task.

So I consider it is soon time for an official 1.0 release!

Or is it?

I mean, there are still several open issues (enhancements only, not bugs), some of which might be worth having fixed in the 1.0 version. Of course, willing to fix these issues would then mean postponing the official 1.0 release (might be end November -or later, depending on how many issues we want solved- instead of early November).

So I ask the question here (I have also asked it there actually): what do you, DesignGridLayout users, prefer?
  1. Version 1.0 in about one week, all current issues open solved only in later versions?
  2. Version 1.0 with more issues closed (which ones?), but released later (between mid November and end December, depending on which issues to solve)
If you don't know, then I'll choose on my own (but I have to admit that I am not sure about what I prefer).

For the time being, even if you don't want to answer this question, you can always check the latest snapshot and report problems if you find any. Also, some of the new API method names I have chosen may be argued (I lacked inspiration for some), so don't hesitate to comment also on these (but please suggest a replacement for the new methods which name you don't like).

Enjoy!

Thursday, October 09, 2008

DesignGridLayout: real-time resizing of JScrollPane

Hi,

In my quest to solve the issue #5 of DesignGridLayout (namely: "Layout does not allow additional height usage after resize"), I have created a special dialog for testing my fixes for this issue. You can see a snapshot below (at default -ie preferred- size).


In this sample, I have put several rows, containing various kinds of components, of which some should be given extra height when the user resizes the dialog (eg JTable), and some should never grow taller than their preferred size (eg JTextField).

I could find out that there are only 2 categories of components that want extra height when it becomes available:
  • any Component that is set as the view of a JScrollPane (in particular JTextArea, JTable, JList)
  • any JSlider using JSlider.VERTICAL policy
Besides these, I did not find any component that should grow height when its embedding dialog is resized.

Based on these observations, I have implemented a simple internal mechanism into DesignGridLayout for distinguishing these 2 kinds of components:
interface HeightGrowPolicy
{
/**
* Checks if a {@link Component} can grow in height.
* @param component the component to test
* @return {@code true} if {@code component} has a variable height;
* {@code false} if {@code component} has a fixed height.
*/
public boolean canGrowHeight(Component component);
}
This interface is implemented by several classes:
  • HeightGrowPolicyMapper (allows to map different Component classes to their own specific HeightGrowPolicy),
  • JScrollPaneHeightGrowPolicy (special implementation for JScrollPane),
  • JSliderHeightGrowPolicy (special implementation for JSlider)
The mechanism itself is easily extensible because I can easily add further policies for other kinds of Components.

My first working prototype for issue #5 was roughly that simple (of course I also had to make some little changes in a few existing classes of DesignGridLayout library).

But I was not fully satisfied with it. The main reason for this was that when you extend the height of the dialog, all resizable components get a few pixels more for their height, which is absolutely ugly for a JTable, a JList or a JTextArea, because these components would then start to show, on the bottom side, some "incomplete" row or line of text.

Whenever I see this happening in a GUI application (be it made in Java or any other language) I generally get angry and immediately classify it in the category of "non professional" software.

So I inferred on some way to solve this: that is really the responsibility of a LayoutManager to make sure that whenever you resize a Container, all resized Components keep good-looking.

First I have extended the interface HeightGrowPolicy above:
interface HeightGrowPolicy
{
public boolean canGrowHeight(Component component);

/**
* Computes the maximum amount of extra height that a {@link Component} can
* use.
* @param component the component to test
* @param extraHeight the amount of available extra height
* @return the maximum amount of extra height that {@code component} can use
* without exceeding {@code extraHeight}
*/
public int computeExtraHeight(Component component, int extraHeight);
}
to give a chance to let DesignGridLayout know what amount of extra height a given Component will accept to keep its good look. This amount can be anything between 0 and extraHeight.

Here is the implementation for JScrollPane components:
class JScrollPaneHeightGrowPolicy implements HeightGrowPolicy
{
public boolean canGrowHeight(Component component)
{
return true;
}

public int computeExtraHeight(Component component, int extraHeight)
{
JScrollPane scroller = (JScrollPane) component;
int unit = scroller.getVerticalScrollBar().getUnitIncrement(+1);
if (unit <= 0)
{
return extraHeight;
}
else
{
// Return an integral number of units pixels
return (extraHeight / unit) * unit;
}
}
}

Simple isn't it? It makes use (behind the scenes) of the Scrollable interface that is most often implemented by components that are supposed to be used in JScrollPane (namely JList, JTable, JTextArea and JTree).

Provided that the preferred size of these components of your dialog is already good looking, then DesignGridLayout will ensure that they always stay this way. If you want to have proper preferred size for JTable, JList or JTextArea, you can use specific API of these components in your own code as in the following snippet:
JTextArea area = new JTextArea();
JTable table = new JTable();
JList list = new JList();

// area has preferred height to show exactly 3 lines of text
area.setRows(3);

// table has preferred height to show exactly 4 rows
int height = 4 * table.getRowHeight();
table.setPreferredScrollableViewportSize(new Dimension(PREF_WIDTH, height));

// list has preferred height to show exactly 2 items (ie 2 rows)
list.setVisibleRowCount(2);

You can try this on the Java Web Start enabled example.

Not bad!

But that's not the end of the story yet. If you play a bit with the example above, you'll see that between two actual resize of e.g. the first JTable component, the spacing between that row and the next is increasing, which is in contradiction with DesignGridLayout philosophy which promises to use the "ideal" inter-components spacing (according to the LAF/platform in use).

So I have made a second attempt, that you can experiment through this JWS example.

In this attempt, inter-components spacing is always preserved. However, to my viewpoint, resizing does not have a very good behavior (in real-time I mean):
  • first of all, the user does not "feel" that something is going on during the first pixels of his resizing action: the layout does not change at all! The user may just stop here and think that resize does not work!
  • second, a weird behavior is observed when the exact number of extra pixels is obtained during resize: all components below the first JTable seem to "hop" to a new position several dozens of pixels away from their previous one!
Hence I believe I will stick with the first solution and completely remove the second one. I could make it an option for the library users to choose, but I would not feel satisfied of giving such a possibility to create layouts with such a bad feel during resize.

What do you think?

If you want to look at DesignGridLayout code which snippets have been used in this post, you should check out the latest trunk from subversion.

Have fun!

Tuesday, September 30, 2008

Flash news: RTL support for DesignGridLayout

Tonight, I have just committed into Subversion the support of right-to-left orientation for DesignGridLayout. as a fix for issue #9.

Implementation was easier than foreseen, except for the test part itself (see below).

First of all, here are 2 screenshots with the same layout, but one uses LTR, the other uses RTL.





DesignGridLayout automatically discovers the orientation to be used for the container it is in charge of laying out, and simply inverts x coordinates if container is RTL-based.

Determining component orientation is based on the Component#getComponentOrientation() method which returns a ComponentOrientation instance. This instance determines the orientation of the text, it is not limited to horizontal LTR (eg English) and RTL (eg Arabic) languages, but also defines languages that are written vertically (all from top to bottom) and which "lines" (should we say "columns"?) are written from right to left (eg Chinese, Japanese) or left to right (eg Mongolian).

However, as far as I know, Swing components LAFs do not support components for vertical languages (I have never seen a vertical JLabel or JTextField), hence for these languages, we have to revert to the "usual" horizontal LTR orientation. For this, DesignGridLayout needs a simple trick to determine text orientation:
ComponentOrientation orientation = parent.getComponentOrientation();
boolean rtl = orientation.isHorizontal() && !orientation.isLeftToRight();

Just using orientation.isLeftToRight() is not sufficient because it would render eg Japanese horizontally from right to left, which I doubt any Japanese person can read (just imagine reading English from right to left!).

As mentioned above, implementing this enhancement was quite easy, but writing test cases for it was much more difficult than I expected.

My original idea for tests was just to set the default Locale to one of English, Arabic, Japanese or Mongolian (in order to cover the four possible situations defined in ComponentOrientation). But it turned out that just creating a new Locale through new Locale("JA") will work only if you have this Locale installed with your JRE, else a new Locale will be instantiated but it will be unusable; in particular, ComponentOrientation.getOrientation(Locale locale) will not return the expected value.

Since my JRE does not include Arabic, Hebrew, Japanese, Chinese and Mongolian, I had to find another way for testing. I chose to directly create a ComponentOrientation instance, but this is not possible since it has no public constructor and it is declared final! The only way to directly use ComponentOrientation is to use one of the 2 provided static instances LEFT_TO_RIGHT and RIGHT_TO_LEFT, which is what I did. But it prevented me from testing the new DesignGridLayout RTL support with vertical orientations.

Hence my call to DesignGridLayout users in Japan or China (for vertical right to left) and Mongolia (for vertical left to right) for testing the latest DesignGridLayout (in subversion trunk) with their respective Locale and send me a screenshot of their results. Thanks in advance!

That'll be all for today.

Sunday, September 28, 2008

Using DesignGridLayout 0.9 in Maven projects

Yesterday, I have released the latest official version of DesignGridLayout, almost 2 years after the previous official release (!), and 4 months after being handed over the project (Jason is still project owner but he is much too busy currently).

As mentioned in my previous post, lots of things have changed between these 2 versions.

Maybe one exciting news is that now, Maven users don't have to struggle to use DesignGridLayout in their projects. Indeed, version 0.9 is now uploaded to a maven repository accessible to anyone (no more need to manually install the jar into your local repository).

However, since I didn't use Maven "central" repository (the conditions for uploading an artifact there are too strict: all your dependencies must be present in central repo too), I wanted to briefly show how to use DesignGridLayout in your maven-based project.

DesignGridLayout artifacts have been uploaded to java.net maven repository. Hence you have to add this repository to your pom.xml (you may also add it to settings.xml if you prefer):
<repositories>
<repository>
<id>maven2-repository.dev.java.net</id>
<name>Java.net Repository for Maven</name>
<url>http://download.java.net/maven/2/</url>
<layout>default</layout>
</repository>
</repositories>

Now you just have to declare the dependency as any other artifact used by your project:
<dependencies>
<dependency>
<groupId>net.java.dev.designgridlayout</groupId>
<artifactId>designgridlayout</artifactId>
<version>0.9</version>
</dependency>
</dependencies>

And voila! You are now ready to use DesignGridLayout for your GUI project!

In addition to DesignGridLayout jar artifact, the repository also contains javadoc and source artifacts that you can automatically download, eg for your IDE (for eclipse, you can follow the instructions there).

Note that swing-layout.jar, the only dependency of DesignGridLayout, will be automatically downloaded to your local repository.

That's it for today, enjoy!

Thursday, September 25, 2008

DesignGridLayout news

Two months ago, I had announced my participation to the OSS project DesignGridLayout.

As a brief reminder, DesignGridLayout is a LayoutManager for Java Swing GUI, which main advantages are:
  • Good looking dialogs (alignment, spacing, sizing, visual balance): this is taken over by DesignGridLayout itself without any special hint from the developer
  • Reduced learning curve for developers, thanks to the fluent API which is, at the same time, simple, effective, compile-safe (no cryptic strings to express the layout) and IDE code-completion friendly
  • No graphics designer needed: the API is the graphical designer
  • Readability and maintainability: you can literally "visualize" the layout by browsing the code that sets it up; inserting a new row of components is done by inserting a new line of code in your layout setup code...
During the past 2 months, after struggling with the switch from CVS to SVN, I have finally finished taking the project over.

In the past few days, I have checked in the latest source code into SVN, and updated the web site.

Several things have changed in this project (as compared with previous 0.1.1 version):
  • License: the original GPL has been changed to ASL 2.0, much more open
  • Build: now the project uses maven 2 for the build and the site generation
  • Package: the old "zappini.designgridlayout" has been changed to a more standard "net.java.dev.designgridlayout"
  • Source code: it has been refactored to improve the API and ease future evolutions
  • Issues: all known bugs have been fixed
  • API: it has been improved on several points (more on this below) such as its narrowing (in order to prevent calls that have no effect, hence potentially pollute source code using DesignGridLayout), as well as the implementation of a few enhancements
  • Javadoc: has been completely rewritten and completed for all public API, along with examples in the package description
Improvements on the API consist essentially in using different interfaces for the different "rows" created by DesignGridLayout:
  • IRow
  • IGridRow extends IRow
  • INonGridRow extends IRow
These interfaces define the exact methods available to each kind of row. Once a row has been created, you cannot change its type (e.g. from Grid to Center) as you could before, which was useless, required more calls and led to some flaws in the API (e.g. what should happen if you set the row type to Grid, then back to Center; and why would you do that?).

Moreover, in DesignGridLayout, methods that create rows have been specialized to determine upfront which kind of row is to be created:
  • DesignGridLayout#row() creates a grid row (IGridRow)
  • DesignGridLayout#centerRow() creates a non grid row, with centered components (INonGridRow)
  • DesignGridLayout#leftRow() creates a non grid row, with left-aligned components (INonGridRow)
  • DesignGridLayout#rightRow() creates a non grid row, with right-aligned components (INonGridRow)
  • DesignGridLayout#emptyRow(int height) creates an empty row, with no component at all (used for introducing vertical spacing between rows)
Other changes in the API are:
  • Removal of IGridRow#label(String) to keep only IGridRow#label(JLabel): this was motivated by the fact a LayoutManager should not create components by itself (arguable opinion, I admit); in addition, this reduces one's options for GUI i18n (one option is to use Component#getName()) to set its text, which is impossible here, since the end-developer code can not get hold of the created JLabel)
  • Row.EMPTY "component" is replaced by IGridRow#empty() methods
  • New INonGridRow#fill() added to allow extreme components to take all remaining space in the row. This is particularly useful to split groups of rows with a label and a separator (as in Karsten Lentzsch FormLayout)
  • New IRow#addMulti() method to add several components in only one grid column, which is useful when you have components that should always "stick together" (eg a JSpinner and a JLabel indicating a unit of measure)
With all these changes, all constants and enum have been removed from the previous version because they serve no purpose now.

You can find the current snapshot (named "0.9-SNAPSHOT") of this version there.

So a further question is "when will the official 0.9 version be released?". That should be short now, we should expect an official release by mid October, including uploaded artifacts to some maven repository (for developers using maven).

What's the roadmap for 1.0?

There are a couple of enhancements requests submitted here.

The main enhancement planned for 1.0 will be the support for variable height rows that would get extra height during resize; this is particularly useful for rows that include components such as JList, JTable, or more generally any component wrapped in a JScrollPane.

Another interesting feature I would like in 1.0 release is components spanning several rows. This is a particularly useful feature and I know several users have been expecting it. The difficult part here will be to define the right API for that, in order to keep this feature easy to use, easy to visualize and safe (reducing potential errors at runtime by catching them at compile-time).

Finally, 1.0 release may include some attempts at right-to-left languages support. This will depend on several factors.

In any case, if you are a DesignGridLayout user or consider it for your next Swing GUI, please take your chance and participate in the discussions on the 3 issues above, so that DesignGridLayout can keep its spirit while bringing important features common in daily GUI design work.

Of course you can also suggest other enhancements and report bugs if you find any.

You can find more details on DesignGridLayout here.

Enjoy GUI design with DesignGridLayout!