development-of-the-user-interface

Development of Liquiface User Interface

We have written before that we developed a NetBeans plugin called Liquiface. Besides finding Liquibase, which we tried to create a “face” for through this development, an incredibly useful tool, the project was also interesting for us because we hadn’t had any prior experiences in NetBeans plugin development or the use of Java Swing. :) This naturally led to a few surprises, long research, lots of thinking and from time to time triumphant cries. As I was mostly in charge of developing the user interface, now I’d like to share some experiences from this area.

Own “desktop” vs. Wizards

First of all, I’d like to say that if you stray into an unknown area, it is good to find out more about the platform in detail first. This may sound obvious but in real life things are never that clear. In this case for example, if we had found out earlier how well documented the NetBeans development had been with what a well organized FAQ, we could probably have saved ourselves some unpleasant moments.

What happened was that my colleague put together our maven-based netbeans project, I received the main component, the TopComponent created by default, and thought that it would be a piece of cake from here. According to our original plans, we wanted to develop a “desktop-like“ experience within the netbeans tab. In theory, this could easily be achieved by using the Swing components, so I thought, it would be enough for me to focus on studying only these.

Our first screen plan: as it is usually the case, it drastically differs from the final version

The original concept was to establish communication with the user by Internal Frames. I made the first frame and it worked quite well, until I added the second one to it… It turned out that the TopComponent was unable to manage multi-level layers, so even though the frames appeared, their order, i.e. which ones were on top, was always fixed. Well… my first steps in Swing. :) It was where I started a long research on how to solve this problem. I tested the use of Desktop Pane extensively, since this was the component designed for the function we wanted, and even though there were traces of possible success, I could not make it work within our TopComponent. I’m pretty sure that if I had had more experiences in the development of desktop applications, sooner or later I could have made it work. But before it could have happened it began to dawn on me that my basic concept was essentially wrong. Because what was it that I wanted? To communicate with the user. And this is often done by the NetBeans itself. What happens for example, when I want to create a new file, a new project, etc.? A wizard pops up. So this is exactly what I should do!

I’ve figured out, that instead of creating a practically independent application interface to be opened in NetBeans, it is a much better solution to actually integrate our interface: that is to use the opportunities offered by NetBeans Api. After this realization, it was easy to find the necessary information on the use of wizards. To my greatest joy, I’ve received an absolutely comfortable and easy-to-use tool with an additional great advantage, that is it also fits in the visual style of NetBeans.

Button in the Swing Table

Another interesting experience was adding the table supporting the adding of columns to the interface. With a past in Web development, I’ve gotten used to the fact that in practice, table cells are universal containers where I can put whatever I wish. This is true for plain HTML, or PrimeFaces-supported Java Server Faces, or even for the Vaadin. So it was quite a surprise when I first tried to add the delete button to all table rows, and instead of the button itself only the toString of the button appeared.

Table used for summarizing and deleting when adding columns

As it turned out, the problem was due to the fact that the JTable, when integrated, did not support the rendering of the JButton. It has to be equipped with this function by the program developer. Luckily, there were many before me who needed this, so I could easily dig up plenty of descriptions, explanations, examples and actual realizations to the problem. The reason why I still decided to make my own implementation was that the solutions I found generated the button from the base data, while I wanted to implement my own button – together with its entire behavior – already within the TableModel itself. To make this happen I opted for the following solution:


public class TableButtonRenderer implements TableCellRenderer {

    @Override
    public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        return (JButton) value;
    }

}

Since the model returns a JButton to me, which already knows of itself, how it needs to be rendered, it is enough to return it adequately casted.


public class TableButtonEditor extends AbstractCellEditor implements TableCellEditor {

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value,
            boolean isSelected, int row, int column) {
        JButton button = (JButton) value;
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                fireEditingStopped();
            }
        });
        return button;
    }

    @Override
    public Object getCellEditorValue() {
        return null;
    }

}

To make the button clickable, it also has to be editable. And the latter requires a TableCellEditor implementation. It is important that here I add an ActionListener to the buttons, which in case of clicking the button indicates, that we’re done with editing. If we don’t do this, the component gets a little mixed up after deletion, in terms of which button belonging to whichever row is rendered in a given row.


columnsTable.setDefaultRenderer(JButton.class, new TableButtonRenderer());
columnsTable.setDefaultEditor(JButton.class, new TableButtonEditor());

After this all we need to do is to tell the table, that in event of a JButton type element it has to use our implementation, and the delete button is ready to be used. :)

Liquiface menu option

If we already have a good NetBeans plugin, naturally we’d want it to be easily accessible from the interface. This is where the question of menu and menu option comes in. Since within the editing area, the application opens on a new tab, we thought, that the Window menu option will be the best place for us. We use an icon, no sub-menus open, we don’t use fast buttons, the ideal position would be between the Properties and the Output menu options. Under NetBeans 7, we can easily set by an annotation, where we want it to appear. And the parameters of the ActionReference annotation speak for themselves. But seriously. Absolutely straightforward. There is no problem with the path parameter, for us this is the ”Menu/Window”. The position is a little more interesting: we get a detailed description in javadoc, namely, that this is what serves for setting the position. Great, it is a whole number, but how much? And this is a question, the answer to which – if there is one – was very well hidden, as I couldn’t find it. Of course it was not a problem to try and change the numbers until we arrive to the desired position, but this does not seem to be the professional way. It would look much nicer, if I was able to find somewhere, what values exactly belong to the Properties and Output menu options, and I could set the position based on this, trying to chose a number so that if someone else also wanted to place his menu option here, we don’t get tangled up.

Regarding the default values I couldn’t find any information. A great deal of search has lead me to finding out that others have also tried to find an answer to this question (without any luck) (I couldn’t find the link again), or that in theory the NetBeans generates a layout.xml from the annotations, and this is what it uses at run-time. My next attempt was directed on finding the generated layout.xml, but this did not bring any results either. The most I could come up with is that we are also able to create a layout.xml within our own module, and with this in our hands, on the NetBeans project tab, within the layout, opening the <this layer in context> menu, we can see the complete structure. By the way, we cannot find out anything here about the menu options which weren’t defined by us (although we could delete them). This was useful for me in practice as if I modified the value of the position within the annotation, then after a clean-and-build, it was already visible without starting the application, what the new position of the menu option was. A little more convenient, but still guessing.
I have also learned, that if we also want to create sub-menus to our menu option and position these, that won’t be possible for the moment with using only annotations.

So I’ve selected the following solution for the exact setting of our position: upon creating a new Action, the NetBeans wizard asks, where I want to place it. Here I can select the Window menu, and within that as a value of the Position, the following: “Properties – HERE – Output”. Having done this, I arrive to the result, that the value I wanted is a nice round number, 1000. I’ve decided, that the NetBeans probably knows what it’s doing, so I accepted this value. And may anyone else position his own plugin here, then the order will be decided somehow, for example based on the current sun-spot activities…

Positioning of the Liquiface menu option within NetBeans

EventBus

In connection with another project, I have investigated the possibility of communication between surface components unaware of one another, when I’ve first become acquainted with the EventBus solution. We did not actually apply this in that very project, but I’ve felt the urge to try this also, and Liquiface was a good basis for this. EventBus is basically a realization of the publish-subscribe pattern. It’s essence briefly is, that communicating parties do not actually need to know one another, communication does not happen directly: the EventBus serves as a transmitting medium. The receiving party subscribes to the EventBus, and specifies what actual event it is interested in. The transmitting party sends its own “transmission”, that is the event caused by it, to the EventBus, which forwards this to all receivers having subscribed to this given event. In our case this meant that we did not need to hand over the references of our many surface components within the system, it was enough to subscribe them to the bus.

Following the decision to use the EventBus, we only had to decide on which realization to use. There were three implementations that have raised my interest: theEventBus, the SimpleEventBus and the Guava Eventbus projects. Eventually I’ve decided on the Guava solution, as it is light-weight and easy-to-use, well documented and good descriptions can be found for it. Another advantage was that it is regularly updated, whereas for the other two projects there were no new versions for a while. As a disadvantage we could mention that it stores strong references. If anyone is interested in a more detailed comparison, they may find a description here.

All together I must say, that we were satisfied with our choice: the Guava EventBus works well in our case. Of course the greatest advantage of this method is also its greatest disadvantage: as the components are unaware of one another, in case of large and complex systems it would be rather complicated to retrace who, when and why reacts to what. For the moment we didn’t have to face such problems, we only enjoy the benefits: we receive a more clear, nicer looking code eventually. So if you haven’t yet heard of this solution, I can truly recommend looking it up!

Epilogue

The above words are from the pen (or rather the keyboard) of someone, who admittedly has come across Swing components and NetBeans plugin development for the first time, so chances are, that there are better, more clear-cut solutions, than the ones described. For such reasons we are open to any comments, findings, critics that could help us in our further development, or must be said.

Facebook2Twitter0Reddit0Google+1LinkedIn0tumblr