SwingJavaBuilder-1 1
SwingJavaBuilder-1 1
Jacek Furmankiewicz
CONTENTS
Introduction
1.1 Abstract . .
1.2 Preface . .
1.3 License . .
1.4 Installation
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
1
1
1
2
2
Overview
2.1 What is JavaBuilders all about? . . . . . . . . . . . . .
2.2 Why would I use this instead of regular coding by hand?
2.3 What is YAML? . . . . . . . . . . . . . . . . . . . . .
2.4 Compact YAML syntax . . . . . . . . . . . . . . . . .
2.5 Development tools . . . . . . . . . . . . . . . . . . . .
2.6 Benefits . . . . . . . . . . . . . . . . . . . . . . . . . .
2.7 Drawbacks . . . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
6
6
8
9
9
10
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
Core Features
4.1 Obtaining references to created components . . . . . . .
4.2 Hooking up event listeners to Java methods . . . . . . .
4.3 Databinding . . . . . . . . . . . . . . . . . . . . . . .
4.4 Input validation . . . . . . . . . . . . . . . . . . . . . .
4.5 Executing long running methods on a background thread
4.6 Executing multiple methods together . . . . . . . . . .
4.7 Custom progress indicators for long running methods . .
4.8 Domain-specific Implementations . . . . . . . . . . . .
4.9 Internationalization . . . . . . . . . . . . . . . . . . . .
4.10 Enum property values . . . . . . . . . . . . . . . . . .
4.11 Using custom components . . . . . . . . . . . . . . . .
4.12 Custom global commands . . . . . . . . . . . . . . . .
4.13 Build events . . . . . . . . . . . . . . . . . . . . . . .
4.14 Hot deployment of UI components . . . . . . . . . . .
4.15 Logging via SLF4J . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
17
17
18
18
19
21
22
23
24
24
26
26
27
28
28
28
Swing Features
5.1 Overview . . . . . . .
5.2 Component properties
5.3 Actions and menus . .
5.4 Borders . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
31
31
31
31
34
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
11
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5.5
5.6
5.7
5.8
5.9
5.10
5.11
5.12
5.13
5.14
5.15
5.16
5.17
5.18
5.19
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
35
35
35
35
36
36
36
37
37
37
38
38
38
40
41
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
43
43
49
49
49
50
Plugins
7.1 Glazed Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
51
51
55
55
55
55
56
56
ii
Button Group . . . . . . . . . . . . . .
Colors . . . . . . . . . . . . . . . . .
Dimensions . . . . . . . . . . . . . . .
Fonts . . . . . . . . . . . . . . . . . .
Icons and images . . . . . . . . . . . .
JComboBox . . . . . . . . . . . . . .
JDesktopPane . . . . . . . . . . . . .
JFrame . . . . . . . . . . . . . . . . .
JList . . . . . . . . . . . . . . . . . .
JScrollPane . . . . . . . . . . . . . . .
JSplitPane . . . . . . . . . . . . . . .
JTabbedPane . . . . . . . . . . . . . .
JTable . . . . . . . . . . . . . . . . . .
Event handlers . . . . . . . . . . . . .
Customizing BetterBeansBinding logic
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
CHAPTER
ONE
INTRODUCTION
1.1 Abstract
Swing JavaBuilder : making Swing development productive
Just started on using the Swing JavaBuilder and i must say i like it. Just replaced 170 rules of Java code
with only 13 lines YAML Comment posted the JavaBuilders forum
The Swing JavaBuilder is a library whose sole goal is to maximize the productivity of a Swing developer. Its main
goal is tackling all the Swing pain points, in particular the complexity and verbosity of the API and reducing it to the
smallest amount of code possible.
This is accomplished by moving all the boring gruntwork of Swing interface creation to an external YAML file, which
has a 1-to-1 match with a backing Java class (e.g. a JFrame or JPanel) that is built from that file. This allows to follow
a pure MVC pattern where the YAML contains nothing but the view, while the Java class is (mostly) the controller.
As an added bonus, the Swing JavaBuilder offers integrated support for data binding (using Beans Binding), input
validation, background task processing (using SwingWorker) and last but not least, an integrated layout management
DSL built-on top of the amazing MigLayout layout manager
In essence, the Swing JavaBuilder is an aggregator of best-of-breed Swing solutions into one common, integrated
toolkit that makes creating Swing user interfaces a breeze
Note: YAML is a file format that is a superset of JSON. We will cover it in more detail in future chapters. Its very
simple to understand, edit and maintain. Its main advantage over both XML and JSON is the lack of any opening
or closing tags, since it implements hierarchical data relationships via whitespace indentation (similar to the Python
programming language).
1.2 Preface
In 2007 or so, Sun Microsystems announced their JavaFX project, which aimed to deliver declarative UIs and rich
desktop functionality. Unfortunately, in what Ive always believed to be a severely misguided decision, this was
accomplished by introducing a totally new language, instead of enhancing the core Java abilities and the existing
Swing UI toolkit.
I decided that there had to be a middle-of-the-road approach that could give Java UI developers the productivity of
declarative UIs without the need to throw out their current language skills out and focus on an unproved and untested
new language (whose features I wasnt particularly fond of anyway, but thats a different story).
The JavaBuilders project was a result of this desire. It started off with many weeks of research and evaluation of
different options. This resulted finally in the creation of a generic declarative UI based around the YAML format
(which has many advantages over the XML or JSON formats) and the integration of many leading open source libraries
(for features such as databinding or input validation) into one integrated solution.
The Swing JavaBuilder is the first production-ready implementation of the JavaBuilder engine, but its generic nature
allows it to be configured for other UI toolkits as well. In the future a SWT JavaBuilder is planned (and maybe even
GTK+ and Qt versions as well).
I hope its adoption by you and your team will greatly increase your productivity and ensure a long and healthy future
for Java rich client development.
Jacek Furmankiewicz
JavaBuilders Technical Architect
P.S. Many thanks to our code contributors: Alexandre Navarro, Sbastien Gollion.
1.3 License
All JavaBuilders code is released under the business-friendly Apache 2.0 license. It is free to use in all projects, both
open source and commercial.
Third party libraries
The Swing JavaBuilder depends on a number of well known open-source components, all of which are released under
business-friendly licenses such as BSD, Apache or LGPL. We never link to any open source components released
under viral licenses such as GPL. Nevertheless, please make sure to evaluate each third party license with your legal
team to ensure compliance with its terms.
1.4 Installation
1.4.1 Standard
Start off with downloading the latest Swing JavaBuilder ZIP file distribution off the JavaBuilders.org website.
In the root folder you will find the Swing JavaBuilder jar and in the /lib folder you will find all of its dependencies.
Add all of them to your projects classpath.
In the /samples folder you will find a sample application that you can use to get a better understanding of how you can
build complex user interfaces using this library.
1.4.2 Maven
If you are using Maven, you can just point to our custom repository:
<repositories>
<repository>
<id>javabuilders</id>
<url>http://javabuilders.googlecode.com/svn/repo</url>
</repository>
</repositories>
Chapter 1. Introduction
<dependencies>
<dependency>
<groupId>org.javabuilders</groupId>
<artifactId>javabuilder-swing</artifactId>
<version>1.1.0</version>
</dependency>
</dependencies>
When creating a new Maven project, it is recommended that you change the default setup to allow Java and resource
YAML files to be in the same source code folder, instead of being split across the src/main/java and src/main/resources
folders:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</resource>
</resources>
<testResources>
<testResource>
<directory>src/test/java</directory>
<includes>
<include>**/*</include>
</includes>
<excludes>
<exclude>**/*.java</exclude>
</excludes>
</testResource>
</testResources>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<compilerVersion>1.6</compilerVersion>
<source>1.6</source>
<target>1.6</target>
<includes>
<include>**/*.yml</include>
<include>**/*.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
</configuration>
</plugin>
</plugins>
</build>
1.4. Installation
Chapter 1. Introduction
CHAPTER
TWO
OVERVIEW
2.1 What is JavaBuilders all about?
In short any object that is built using a JavaBuilder consists of two files:
a YAML text file that provides a declarative definition of the subject, most commonly the user interface. This
would include items such as the controls that get instantiated, their properties, which methods should be called
from event listeners, layout definition, data binding definition, predefined validations on controls or their properties.
a Java class with all the actual code that represents the object being built. So for example, in Swing JavaBuilder
the Java class may be a JFrame with all the relevant methods (e.g. save(), close(), validateInput(), as well as
public properties that refer to the data being entered/maintained in the window).
Using a convention over configuration approach inspired by the Apache Wicket web framework, both files reside in
the same package and with the same name, but just with a different file extension, e.g.:
MainApplicationFrame.java
MainApplicationFrame.yml
then you can define a YAML file using the DeclaringClass.InnerClass.yml format, e.g.:
CommonPanels.SomePanel.yml
or:
The only time you need to escape into quotes is if your text contains YAML-reserved characters such as :, e.g.:
text: "First name:"
Chapter 2. Overview
]
}
]
}
Scroll to the bottom of this JavaFX code sample to see what I mean: http://jfx.wikia.com/wiki/JFXPresentation
myLabel2
My First Label
myLabel2
My Second Label
However, in most cases you will not be coding in either traditional YAML or JSON. We have enhanced the standard
YAML syntax to make it even more compact (more on that in the next sections). In most cases your YAML content
will look like this:
JFrame(name=myFrame,title=My Frame):
- JLabel(name=myLabel2, text=My First Label)
- JLabel(name=myLabel2, text=My Second Label)
This is still valid YAML syntax and our custom YAML pre-processor takes care of exploding this compact syntax
to the equivalent full YAML content
Maps:
JFrame:
name: myFrame
title: My Frame
Free-form text with new lines preserved (accomplished with the | indicator):
quote: |
To code by hand or not?
There is no question.
You should just be using JavaBuilders.
Will Shakespeare (JavaBuilders early adopter)
The same content can be entered in much less lines using our compact syntax:
JFrame(name=frame,title=My Frame):
- JButton(name=buttonClose,text=Close,onAction=close)
- JButton(name=buttonSave,text=Save,onAction=save)
Lets be clear: this is not part of the official YAML standard. This is something specific to JavaBuilders that was added
to make the YAML file even smaller.
Basic concepts
8
Chapter 2. Overview
properties and their values are entered between ( and ) on the same line as the object they refer to
instead of the default YAML name: value format it uses name=value but it still uses the default YAML
collection indicators [ and ] (e.g. list=[listItem1,listItem2]
if an object has a collection of object defined directly underneath it, they automatically get moved to the default
content node (just as in the example shown above)
Note: All the code samples from this point will use the compact syntax, in order to promote its use.
2.5.1 Eclipse
Eclipse YAML Editor: http://code.google.com/p/yamleditor/
2.5.2 NetBeans
As of NetBeans 6.5 a YAML editor is included in the core distribution.
2.6 Benefits
2.6.1 What are the benefits compared to coding by hand?
You have to write a lot less code. JavaBuilders introduces dynamic language-level productivity (think Ruby/Groovy)
to Java. See this typical Java Swing example:
ResourceBundle bundle = ResourceBundle.getBundle("Resources");
JButton button = new JButton();
button.setName("okButton");
button.setText(bundle.getString("button.ok"));
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
//execute the save method
save();
}
});
and all you need to build this Swing Java class from this YAML file is this single line of code somewhere in your
constructor:
SwingJavaBuilder.build(this);
The equivalent code for any other UI toolkit (e.g. SWTJavaBuilder) would be just as compact.
2.6.2 What are the benefits compared to using GUI Builders, such as NetBeans
Matisse?
Mostly maintainability. For smaller examples its probably not much of a difference (since so much of the code is
generated for you by Matisse), but once you get into larger, more complex forms it becomes harder to maintain them
in a GUI builder, especially if you have to move the layout around a lot. In JavaBuilders, its just a matter of changing
a few lines of text in a YAML file.
Also, e can add custom properties to existing objects, so we can enhance APIs or make them easier, e.g.:
JFrame(size=800x400)
The Swing JFrame class does not have a property called size. But JavaBuilders can support virtual properties
which trigger some Java code that will magically call the proper equivalent methods, in order to achieve the same
functionality in much less code.
Last, but not least, JavaBuilders provide support for functionality not provided by GUI builders, such as integrated
input validators or executing cancellable long running methods on a background thread.
2.7 Drawbacks
Nothing is perfect, so JavaBuilders have weak points too.
Lose some of the static, compile-time safety: since you are defining all the layouts/event wiring in a YAML text
file, some of the referenced objects may have a different name that their corresponding equivalents in the Java
file, especially if using refactoring. This can be overcome with the @Alias annotation, which hardcodes a link
between a Java-side object and its definition in the YAML file.
No code completion (at least not yet). YAML is just a pure text file. You wont know what the known properties
are for any particular object type unless you know them already. But in most cases its the basic ones: name,
text, onAction, onClicked, etc.
You have to get acquainted with YAML...sorry, cant help you there. Sometimes we just need to learn new
things. The bottom line though is that all your code stays in Java, YAML is just used for declarative UI building.
On the upside, UI components built with JavaBuilders are easily unit testable. You just need to do:
new MyComponent()
in your unit test, thats all. When an object is built, the JavaBuilder automatically validates that not only the properties
are defined correctly, but also all the event listeners point to actual existing methods in the Java class. If not, a
BuildException will be thrown right away.
10
Chapter 2. Overview
CHAPTER
THREE
11
5. Create a PersonApp.properties file in the root package with the internationalized resources:
button.save=Save
button.cancel=Cancel
label.firstName=First Name:
label.lastName=Last Name:
label.email=Email
frame.title=Enter Person Data
7. Create the controller Java class person.app.PersonApp (same package where the YAML file is):
package person.app;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
12
import javax.swing.UIManager;
import
import
import
import
import
org.javabuilders.BuildResult;
org.javabuilders.annotations.DoInBackground;
org.javabuilders.event.BackgroundEvent;
org.javabuilders.event.CancelStatus;
org.javabuilders.swing.SwingJavaBuilder;
@SuppressWarnings({"serial","unused"})
public class PersonApp extends JFrame {
private Person person;
private BuildResult result;
public PersonApp() {
person = new Person();
person.setFirstName("John");
person.setLastName("Smith");
result = SwingJavaBuilder.build(this);
}
public Person getPerson() {
return person;
}
private void cancel() {
setVisible(false);
}
@DoInBackground(cancelable = true,
indeterminateProgress = false, progressStart = 1,
progressEnd = 100)
private void save(BackgroundEvent evt) {
// simulate a long running save to a database
for (int i = 0; i < 100; i++) {
// progress indicator
evt.setProgressValue(i + 1);
evt.setProgressMessage("" + i + "% done...");
// check if cancel was requested
if (evt.getCancelStatus() != CancelStatus.REQUESTED) {
// sleep
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
} else {
// cancel requested, lets abort
evt.setCancelStatus(CancelStatus.COMPLETED);
break;
}
}
}
// runs after successful save
private void done() {
JOptionPane.showMessageDialog(this, "Person data: " + person.toString());
}
13
/**
* @param args
*/
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
// activate internationalization
SwingJavaBuilder.getConfig().addResourceBundle("PersonApp");
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
new PersonApp().setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
8. Run the PersonApp.main() method. You should see an input dialog like this appear:
Note: Notice that the default person name is propagated from the Java code to the UI via data binding.
All the controls are created and the layout is executed without the need for an IDE-specific GUI builder. Also, many
of the controls were auto-created without being explicitly defined. Putting a resource name within a String literal
automatically created JLabel instances, while defining a field with a txt prefix automatically created JTextField
instances. All without any additional YAML or Java code.
The resource keys entered in quotes in the layout section have been used to automatically create JLabel(s) and populate
their text with the value of the resource key.
9. Enter an invalid email address for the person and press Save:
14
The validation logic (invoked via $validate) executed and perform basic input validation.
10. Enter a valid email address:
11. Press Save. The save() Java method is executed (which simulates a long running database save with a progress
bar) and since it is annotated with the @DoInBackground annotation it will automatically run on a background
thread using the SwingWorker library.
12. After the save logic executes, the done() Java method is executed to inform the user the save was successful. Notice that the email address we entered was automatically propagated back to the underlying bean using
databinding.
15
13. Press Cancel to close the window. Since you specified $confirm in the action handler, it will automatically
prompt the user to confirm the action. If they select Yes, the cancel() Java method will be called and the
window will close.
Summary
16 lines of YAML
3 simple Java methods to handle save(), done() and cancel() (and without any of the logic to create and layout
the controls)
That is all we needed to create a fully functional application with control creation and layout, data input validation and
executing long running business methods on a background thread via SwingWorker. Not to mention its fully localized
with all the labels being automatically fetched from a ResourceBundle
16
CHAPTER
FOUR
CORE FEATURES
4.1 Obtaining references to created components
Convention over configuration
In most cases, we use a straightforward convention-over-configuration approach. If you define an object in YAML and
then define a Java instance instance variable with the same name and of compatible type, then JavaBuilders will set
the reference on it automatically (even if it is a private variable, it does not need to be public).
Simple example:
MyFrame.yml:
JFrame:
- JButton(name=okButton,text=OK,onAction=save)
MyFrame.java:
public class MyFrame extends JFrame {
//this objects reference will be set automatically
private JButton okButton;
private BuildResult result = SwingJavaBuilder.build(this)
public MyFrame(){
//reference is set! NullPointerException will not occur
okButton.setText("New text");
}
private void save() {
//execute some business logic...
}
}
17
If any of the methods return a boolean false , then the other methods get aborted and will not be called. Simple
convention over configuration approach
method():
private void save() {}
Enter whichever one you want and JavaBuilders will find it and execute it. If it finds multiple ones, it will execute the
first one it finds based on the preference above. If none are found, a BuildException will be thrown right away during
build time. So, you do not have to actually test your event listener logic by manually clicking on the button or menu
item, the validation occurs right away as part of the build process. This simplifies unit testing and limits the risk of
lost type safety.
4.3 Databinding
Binding is defined by adding a bind root node after all the controls have been defined. Unlike in most other languages, the binding is not defined at the property level, but is a stand-alone node of its own. This is done to enforce
separation of concerns and ensure clarity. You can see all your data binding in one place, all together.
Sample (assume we have a backing JFrame JavaBean with two public properties lastName and firstName):
18
JFrame(name=frame,title=Hallo):
- JTextField(name=firstNameField)
- JTextField(name=lastNameField)
- JButton(name=saveButton, text=Save)
- layout: |
[]
[grow]
>"First name:"
firstNameField
>"Last name:"
lastNameField
>saveButton+*
bind:
- this.title : "Hello, ${firstNameField.text}"
- this.firstName : firstNameField.text
Note that you can bind either using an EL expression or directly to an objectName.propertyName.
19
If you want to do it from the Java then you just need to call the validate() method on the BuildResult object that was
returned:
private BuildResult result = SwingJavaBuilder.build(this);
//validate user input
private boolean validate() {
return result.validate();
}
minValue
maxValue
dateFormat
emailAddress
Example
mandatory:true
minLength: 5
maxLength : 5
regex: [a-zA-Z0-9]+
regex: [a-zA-Z0-9]+, regexMessage: {0} must be a number
or letter
minValue: 5
maxValue: 50
dateFormat: yyyy/mm/dd
emailAddress: true
Comment
Full example:
validate:
- mandatory.text: {label: Mandatory Field, mandatory: true}
- date.text: {label: Date Field, dateFormat: "yyyy/mm/dd"}
- email.text: {label: E-Mail, email: true}
- minmax.text: {label: Min/Max Length, minLength: 5, maxLength: 10}
- regex.text: {label: Regex, regex: "[a-zA-Z0-9]+"}
- regex2.text: {label: Regex, regex: "[a-zA-Z0-9]+",
regexMessage: "{0} must be a number or letter"}
- long.text: {label: Min/Max Long, minValue: 5, maxValue: 50, mandatory: true}
20
21
Any method that is annotated as such must implement a signature that accepts an object of type BackgroundEvent ,
which allows the background method to communicate with the UIs progress indicator and even cancel itself, if the
user requests it, e.g.:
@DoInBackground(cancelable=true, progressStart=1, progressEnd=100,
progressValue=1, indeterminateProgress=false)
private void save(BackgroundEvent evt) {
System.out.println("SAVE...");
for(int i = 0; i < 100; i++) {
if (evt.getCancelStatus() != CancelStatus.REQUESTED) {
try {
Thread.currentThread().sleep(100);
evt.setProgressValue(i + 1);
evt.setProgressMessage(String.format("Processing %s of %s...",
evt.getProgressValue(), evt.getProgressEnd()));
} catch (InterruptedException e) {}
} else {
evt.setCancelStatus(CancelStatus.PROCESSING);
System.out.println("Cancelling...");
evt.setCancelStatus(CancelStatus.COMPLETED);
break;
}
}
System.out.println("SAVE END...");
}
within
JButton(text=Save,onAction=[$validate,save,close])
The methods after the long running method (i.e. close in this example), will only execute after the long running
method has finished, they will not run in parallel, even though they are on different threads. Hence, the sequence of
22
events is preserved.
These can be added either at the global level (i.e. for all components) on the builder config, e.g.:
SwingJavaBuilder.getConfig().addBackgroundEventListener(new BackgroundEventListener() {
@Override
public void backgroundTaskStarted(BuildResult r, BackgroundEvent evt) {
//notify common progress indicator about a new background task
}
@Override
public void backgroundTaskEnded(BuildResult r, BackgroundEvent evt) {
//notify common progress indicator that a background task has ended
}
});
23
The BackgroundEvent object itself includes full PropertyChangeSupport, so you can add listeners to monitor its
properties and updated the progress bar min/max/value/message accordingly.
4.9 Internationalization
Internationalizaton support in any Builder is provided at two levels: global and class-level. If any resource bundle is
present (either at the global or class level), the internationalization support will automatically get activated.
or:
ResourceBundle myResourceBundle = ....
SwingJavaBuilder.getConfig().addResourceBundle(myResourceBundle);
The builder will look at the class-level bundles first for a key and if not found, will search through the global ones.
4.9.3 Usage
Once you register a resource bundle, you can pass a resource name directly to any of the properties that have been
flagged as localizable, e.g.
YAML:
JButton(name=okButton, text=button.ok)
Properties file:
24
button.ok=OK
Default translations in French and Italian are provided as well. If you want to override any of these messages or
provide an additional locale translation, you just have to override any of these keys in any of the resource bundles that
you have registered.
The library will look for these keys in your bundles first before falling back on the built-in one.
You can have them marked explicitly with # (e.g. #button.doThis#) to further visually indicate that they are mising
by calling the setMarkInvalidResourceBundleKeys(boolean) method, e.g.:
SwingJavaBuilder.getConfig().setMarkInvalidResourceBundleKeys(true);
4.9. Internationalization
25
then you can still do either the original constant value or the camel-case named equivalent:
JXFrame(startPosition=CenterInParent)
or:
JXFrame(startPosition=centerInParent)
or:
JFrame(defaultCloseOperation=exitOnClose)
YAML:
JFrame(title=frame.title,state=max,defaultCloseOperation=exitOnClose):
- ComponentsPanel(name=componentsPanel,tabTitle=tab.components)
- BorderPanel(name=borderPanel,tabTitle=tab.borders)
- CardLayoutPanel(name=cardLayoutPanel,tabTitle=tab.cardLayout)
- FlowLayoutPanel(name=flowLayoutPanel,tabTitle=tab.flowLayout)
- MigLayoutPanel1(name=migLayoutPanel1,tabTitle=tab.migLayout1)
Java:
private
private
private
private
private
ComponentsPanel componentsPanel;
FlowLayoutPanel flowLayoutPanel;
CardLayoutPanel cardLayoutPanel;
MigLayoutPanel1 migLayoutPanel1;
BorderPanel borderPanel;
$confim : displays a standard Are you sure? confirmation dialog that can be invoked before any destructive
action:
JButton(name=deleteButton, text=Delete, onAction=[$confirm,delete])
27
Thats it! Now the builder will read the YAML files from the source folder, instead of the bin folder, meaning you
can keep editing them while the app is running and immediately see the changes as soon as you re-open the current
component you were working on.
28
29
30
CHAPTER
FIVE
SWING FEATURES
Now that weve seen the core JavaBuilderss features, lets explore what the Swing JavaBuilder provides on top of that
for building actual Swing user interfaces.
5.1 Overview
The Swing JavaBuilder is an instance of the JavaBuilders engine, pre-configured for use with the Swing UI toolkit. It
is represented by the main class org.javabuilders.swing.SwingJavaBuilder and in most typical cases
that is the only class you will be dealing with.:
public class MyFrame extends JFrame {
private BuildResult result = SwingJavaBuilder.build(this);
public MyFrame() {}
}
The returned BuildResult obtain contains a reference to the various objects that were created during the build
process, but it is often not necessary to interact with it at all (unless you are doing something more complex or
custom).
However, some components have been enhanced in the Swing JavaBuilder to make instantiating and using them even
easier.
31
The sample above sets the text to Save, the mnemonic on the S character and the accelerator to Ctrl+S.
Valid accelerators are:
1. Ctrl
2. Alt
3. Shift
4. Meta
followed by the appropriate character. They can be mixed together, e.g. Ctrl+Alt+N. Due to the embedded \t,
such menu definitions have to be escaped into quoted text, as per the example above.
5.3.3 Actions
The regular Swing Action API has been modified separately to separate the concept of name vs text (which are the
same in the Action API, but we treat them separately so that the text can be easily internationalized, without affecting
the name). It provides name, text, toolTipText, icon properties and the name of the Java method to be invoked is
defined in the onAction handler.
YAML:
Action(name=newAction, text=menu.file.new, icon=images/document-new.png, onAction=onFileNew)
Java:
private void onFileNew() {
System.out.print("onFileNew was invoked!");
}
Any descendant of AbstractButton (such as JMenuItem or JButton can then refer to it in its action property, e.g.:
32
- JMenuItem(action=newAction)
- JMenuItem(action=openAction)
- JSeparator()
- JMenuItem(action=saveAction)
- JSeparator()
- JMenuItem(action=exitAction)
- JMenu(name=optionsMenu, text=menu.options):
- JRadioButtonMenuItem(name=radio1Menu, action=option1Action)
- JRadioButtonMenuItem(name=radio2Menu, text=menu.option2)
- JRadioButtonMenuItem(name=radio3Menu, text=menu.option3)
- ButtonGroup: [radio1Menu, radio2Menu, radio3Menu]
- JSeparator()
- JCheckBoxMenuItem(text=menu.option1, onAction=option1)
- JCheckBoxMenuItem(text=menu.option2)
- JCheckBoxMenuItem(text=menu.option3)
- JMenu(name=helpMenu,text=menu.help):
- JMenuItem(action=helpAboutAction)
5.3.5 JPopupMenu
Popup menus can easily be added to any Swing component by simply specifying the popupMenu property to point
to an existing JPopupMenu instance by name. The Swing JavaBuilder takes care of all the mouse event wiring to
popup the menu upon right-click.
With actions:
- Action(name=copyAction, text=menu.edit.copy, onAction=copy)
- Action(name=pasteAction, text=menu.edit.paste, onAction=paste)
- JPopupMenu(name=popup):
- JMenuItem(action=copyAction)
33
- JMenuItem(action=pasteAction)
- JTabbedPane(name=tabs, onChange=onTabChanged):
- JPanel(name=frameYamlSource, tabTitle=tab.frameYamlSource):
- JScrollPane(name=scroll1):
JTextArea(name=frameSourceArea, popupMenu=popup)
Without actions:
- JPopupMenu(name=popup):
- JMenuItem(name=popupCopy, text=Copy, onAction=copy)
- JMenuItem(name=popupPaste, text=Paste, onAction=paste)
- JTabbedPane(name=tabs, onChange=onTabChanged):
- JPanel(name=frameYamlSource, tabTitle=tab.frameYamlSource):
- JScrollPane(name=scroll1):
JTextArea(name=frameSourceArea, popupMenu=popup)
5.4 Borders
5.4.1 Regular Borders
Any Swing component that allows setting of borders can do it by using a set of pre-defined constants:
loweredBevel
raisedBevel
loweredEtched
raisedEtched
Example:
JPanel(name=panel1, border=raisedBevel)
Note: groupTitle is internationalizable, so you can pass a resource key to it, instead of a hard-coded String.
34
5.6 Colors
Colors can be specified using a standard HTML/CSS style syntax. Valid values are:
Hex Color:
JTextArea(name=textArea, background=ff00ee)
5.7 Dimensions
Dimension (java.awt.Dimension) values can be specified using a width x height syntax, e.g:
MyCustomPanel(size=800x400)
5.8 Fonts
Fonts can be specified using a CSS-like syntax: bold|italic size name:
35
JButton(font=italic)
JButton(font=italic bold)
JButton(font=italic 14pt)
JButton(font=Arial)
JButton(font=italic bold 14pt Arial)
Alternatively, if you initialized the builder with a ResourceBundle to activate internationalization, you can pass a
resource key instead. The builder will look for the path to the image via the key in the bundle instead, e.g.:
YAML:
JMenuItem(text=menu.save, icon=images.saveDocument)
Properties file:
images.saveDocument=/myapp/resources/images/document-save.png
5.10 JComboBox
5.10.1 Databinding
In order to bind a List to a JComboBox, you need to bind it to its model property, e.g.:
bind:
- jComboBox.model: this.books
An alternate (and arguably more powerful) databinding method involves using the GlazedLists library, please refer to
the relevant chapter for more details.
5.11 JDesktopPane
5.11.1 JInternalFrame integration
A JDesktopPane can be placed in a JFrame or a regular JPanel, followed by one or more instances of a
JInternalFrame, e.g.:
JPanel:
- JDesktopPane(name=desktop,dragMode=outlineDragMode,visible=true):
- JInternalFrame(name=frame1,title=Frame 1,visible=true,selected=true):
- JButton(name=button1,text=Button 1)
- JLabel(name=label1,text=Label 1)
- MigLayout: |
[grow] [pref]
label1 button1
36
- JInternalFrame(name=frame2,title=Frame 2,visible=true):
- JButton(name=button2,text=Button 2)
- JLabel(name=label2,text=Label 2)
- MigLayout: |
[grow] [pref]
label2 button2
- MigLayout: |
[grow]
desktop
[grow]
5.12 JFrame
JFrame support in the Swing JavaBuilder adds custom processing for the following properties:
size
Can be in width_x_height format (e.g. 800x400) or packed to indicate the JFrame.pack() method should
be called at the end (after all the child components have been added), e.g.:
JFrame(size=800x400)
JFrame(size=packed)
state
Allows setting the extended state of a frame, valid values are:
max
maxh
maxv
icon
JFrame(state=max)
5.13 JList
5.13.1 Databinding
In order to bind a List to a JList, you need to bind it to its model property, e.g.:
bind:
- jList.model: this.books
An alternate (and arguably more powerful) databinding method involves using the GlazedLists library, please refer to
the relevant chapter for more details.
5.14 JScrollPane
Used to wrap components in a scrollable pane. Since it only has one child underneath, it is entered not as a YAML
list, but a single item:
5.12. JFrame
37
JScrollPane(name=scrollPane1, verticalScrollBarPolicy=asNeeded,horizontalScrollBarPolicy=asNeeded):
JTextArea(name=textArea)
You can also use the shorter vScrollBar and hScrollBar aliases:
JScrollPane(name=scrollPane1, vScrollBar=asNeeded, hScrollBar=asNeeded):
JTextArea(name=textArea
5.15 JSplitPane
In order to use a JSplitPane just list the child components underneath it. The first two will be automatically added as
the left/right (or top/bottom) panes,e.g.:
JPanel:
- JSplitPane(name=split1,orientation=verticalSplit):
- JCustomPanel1(name=panel1)
- JCustomPanel2(name=panel2)
The orientation propertys verticalSplit or horizontalSplit values define the type of split.
5.16 JTabbedPane
In order to create tab pages, just list the controls you wants as tabs underneath the JTabbedPane node. In order to
specify the tab title, tooltip and icon use the following properties:
tabTitle (localizable)
tabToolTip (localizable)
tabIcon
tabEnabled
Those are used only if a component is listed underneath a JTabbedPane and are ignored if used anywhere
else.:
- JTabbedPane(name=tabs):
- JPanel(tabTitle=tab.frameYamlSource, tabIcon=images/tab1.png)
5.17 JTable
5.17.1 Custom Table Models
You can integrate custom table models into your JTables. First, you must register your custom model (usually in the
main(), so that the Swing JavaBuilder engine is aware of it, e.g.:
38
SwingJavaBuilder.getConfig().addType(MyCustomTableModel.class);
Note: Your custom table does not actually need to have name property. If it does not exist, the Swing JavaBuilder
will handle it as a virtual property. A named instance of the model (that you can manipulate from the Java code) will
be created, e.g.:
private MyCustomTableModel model;
Also, please read the GlazedLists chapter on information on some custom GlazedLists table models that are integrated
into the Swing JavaBuilder as an optional plugin.
When processing the list of table columns, the builder will evaluate the columns that are there already. If it can match
based on the identifier or headerValue then it will use that existing columns, otherwise it will create a new one and add
it to the JTable.
or you can define an explicit JCheckBox, JComboBox or JTextField underneath it. In this case the builder will
automatically wrap it with a DefaultCellEdior wrapper:
JTable(name=table1):
- TableColumn(name=col1,resizable=true, headerValue=Column 1):
- JComboBox(name=col1Box)
- TableColumn(name=col2,resizable=true, headerValue=Column 2):
- JCheckBox(name=col2Box)
- TableColumn(name=col3,resizable=false, headerValue=Column 3):
- JTextField(name=col3Field)
5.17. JTable
39
If you want to define a column header renderer, just add a forHeader=true property:
JTable(name=table1):
- TableColumn(name=col1,resizable=true, headerValue=Column 1):
- MyCustomRenderer(name=col1Renderer, forHeader=true)
Action
Event Name
onAction
Event Class
ActionEvent
(Abstract) Button
onAction
ActionEvent
Component
onFocus
onFocusLost
onKeyPressed
onKeyReleased
onKeyTyped
onMouseClicked
onMouseDoubleClicked
onMouseDragged
onMouseEntered
onMouseExited
onMouseMoved
onMousePressed
onMouseReleased
onMouseRightClicked
onMouseWheelMoved
40
FocusEvent
FocusEvent
KeyEvent
KeyEvent
KeyEvent
MouseEvent
MouseEvent
MouseEvent
MouseEvent
MouseEvent
MouseEvent
MouseEvent
MouseEvent
MouseEvent
MouseWheelEvent
Chapter 5. Swing Features
JComboBox
onAction
ActionEvent
JFrame
onStateChanged
onWindowActivated
onWindowClosed
onWindowClosing
onWindowDeactivated
onWindowDeiconified
onWindowFocus
onWindowFocusLost
onWindowIconified
onWindowOpened
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
JTabbedPane
onChange
ChangeEvent
JTable
onSelection
ListSelectionEvent
JTextField
onAction
ActionEvent
JTree
onSelection
TreeSelectionEvent
Window
onStateChanged
onWindowActivated
onWindowClosed
onWindowClosing
onWindowDeactivated
onWindowDeiconified
onWindowFocus
onWindowFocusLost
onWindowIconified
onWindowOpened
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
WindowEvent
41
Since BuildResult is generic across all domains, it returns an Object from that method. It needs to be cast explicitly to
BindingGroup when working in Swing.
BindingGroup group = (BindingGroup)result.getBindingContext();
42
CHAPTER
SIX
43
MigLayout: |
[[insets 8]
[pref]
[grow]
>"First name:"
firstName
>"Last name:"
lastName
>okButton+*,cancelButton
[growy, bottom]
{okButton: tag OK, cancelButton: tag Cancel}
From this you can probably see right away that we have 3 rows (as in 3 lines of text), the labels are in the same vertical
column, the text fields are in the same vertical column (which is flagged to grow, a standard MigLayout constraint).
#optional
#optional
[row constraint - o
#optional
6.1.4 Alignment
Goes before the control name, e.g. >fieldNameLabel. If none are presents it defaults to top, left.
<
|
>
^
/
44
#
#
#
#
horizontally
horizontally
horizontally
horizontally
span
span
span
span
Minimum width
Preferred width
Maximum width
Minimum height
Preferred height
Maximum height
Control Type
JButton
JToggleButton
JTextField
JCheckBox
JRadioButton
JComboBox
JList
JTextArea
JTable
JTree
JSlider
JProgressBar
JPasswordField
JSpinner
JSeparator
45
Note: for JButton and JToggleButton, the suffix will be used to find label and map onAction to Java method.
E.g. btnApply will generate a JButton, with a text Apply and the onAction listened wired to the equivalent
apply().
Heres a fully verbose example:
JFrame(name=frame, title=frame.title, size=packed, defaultCloseOperation=exitOnClose):
- JLabel(name=fNameLbl, text=label.firstName)
- JLabel(name=lNameLbl, text=label.lastName)
- JLabel(name=emailLbl, text=label.email)
- JTextField(name=fName)
- JTextField(name=lName)
- JTextField(name=email)
- JButton(name=save, text=button.save, onAction=[$validate,save,done])
- JButton(name=cancel, text=button.cancel, onAction=[$confirm,cancel])
- MigLayout: |
[pref]
[grow,100] [pref]
[grow,100]
fNameLbl
fName
lNameLbl lName
emailLbl
email+*
>save+*=1,cancel=1
And here is what it would look like after using control auto-creation (to create all the JTextField instances by
prefixing the control name with txt):
JFrame(name=frame, title=frame.title, size=packed, defaultCloseOperation=exitOnClose):
- JLabel(name=fNameLbl, text=label.firstName)
- JLabel(name=lNameLbl, text=label.lastName)
- JLabel(name=emailLbl, text=label.email)
- JButton(name=save, text=button.save, onAction=[$validate,save,done])
- JButton(name=cancel, text=button.cancel, onAction=[$confirm,cancel])
- MigLayout: |
[pref]
[grow,100] [pref]
[grow,100]
fNameLbl
txtFName
lNameLbl txtLName
emailLbl
txtEmail+*
>save+*=1,cancel=1
Notice that we did not have to explicitly define the thre JTextField entries any more. Just by calling the controls
txtFName,txtLName,txtEmail the builder knew you wanted to create controls of type JTextField and did
that for you.
Note: Notice the special handling of JButton: the buttons name suffix (e.g. btnOK = OK, btnDelete
= Delete) will be converted to Pascal case and a corresponding Java method (e.g. ok(), delete()) will be
expected to be present in the Java class so that onAction can be wired automatically.
Warning: if you create a btnNew JButton that would automatically map to a Java method new(), which is
a reserved keyword and cannot be compiled. In this corner case we will try to match it to a doNew() method
instead.
46
HTML content
It is possible to enter HTML tags in the label, in the format accepted by JLabel. It is not required to put in the
<html/> tag around it, Swing JavaBuilder will do it automatically if it encounters HTML tags in the text, e.g.:
JFrame(name=frame, title=frame.title, size=packed, defaultCloseOperation=exitOnClose):
- MigLayout: |
[pref]
[grow,100] [pref]
[grow,100]
"<b>First name:</b>"
txtFName
"<i>Last name:</i>:" txtLName
And we just refer to that prototype by prefixing its name with $ in the MigLayout section, e.g.:
- MigLayout: |
[grow]
"Name:"
$btnAdd
[pref]
txtName
The $btnAdd control will get auto-created defined the prototype definition we saw earlier.
If later you decided to change something about it (e.g. new icon, new text, add tooltip, etc.) all you have to do is
change it in one place and it will get reflected throughout the whole application.
47
YAML:
JPanel:
- JScrollPane(name=scroll1): JTextArea(name=source,font=Monospaced,editable=false)
- MigLayout: |
[200,grow]
[right]
[200,grow]
[200,grow]
scroll1+1+*
"Last name:"
txtLName
"First Name"
txtFName
48
"Phone:"
txtPhone
"Email:"+2,txtEmail
"Address 1:"
txtAddress1+*
"Address 2:"
txtAddress2+*
"City:"
txtCity
"State:"
txtState
"Postal Code:"
txtPostal
"Country:"
txtCountry
^|btnNew+*=1,^btnDelete=1,^btnEdit=1,^btnSave=1,^btnCancel=1 [grow]
6.2 MigLayout
If for whatever reason you do not want to use the MigLayout DSL, you can still use regular MigLayout properties and
syntax, e.g.:
JFrame(title=My Frame):
content:
- JLabel(name=firstNameLabel,text=First Name)
- JTextField(name=firstName)
- JLabel(name=lastNameLabel,text=Last Name)
- JTextField(name=lastName)
- JButton(name=okButton)
- MigLayout:
layoutConstraints: wrap 2
columnConstraints: [] [grow] []
rowConstraints: [] [] []
constraints:
- firstNameLabel: right
- firstName: 200px, sg 1
- lastNameLabel: right
- lastName: 200px, sg 1
- okButton: span, right, tag ok
But we recommend you always use the DSL syntax instead, its much more powerful and easier to use after the initial
learning curve.
6.3 CardLayout
CardLayout support is provided by adding a CardLayout node at the end of the list of child components, e.g.:
JPanel:
- JPanel(name=panel1)
- JPanel(name=panel2)
- CardLayout(name=cards): [panel1,panel2]
By default the card name is the same as the name of the control that was added as a card. Using the name
property you can get a handle to the created instance of CardLayout in your Java-side code, e.g.:
private CardLayout cards;
6.4 FlowLayout
In order to use FlowLayout, just create a FlowLayout node at the end of the list of child components. No need to
specify which ones to add, they all get added automatically, e.g.:
6.2. MigLayout
49
JPanel:
- JPanel(name=panel1,groupTitle=Flow layout components):
- JLabel(text=Label 1)
- JButton(text=Button 1)
- JLabel(text=Label 2)
- JButton(text=Button 2)
- JLabel(text=Label 3)
- JButton(text=Button 4)
- JLabel(text=Label 5)
- JButton(text=Button 5)
- FlowLayout(alignment=left,hgap=30,vgap=30,alignOnBaseline=true)
50
CHAPTER
SEVEN
PLUGINS
The core Swing JavaBuilder library can be extended via plugins that provide integration with best-of-breed external
Swing libraries
Below you will find information on how GlazedLists are integrated into the JavaBuilders library.
7.1.1 JList
EventListModel
The JList-specific model in GlazedLists is called EventListModel. You will need to define a GlazedLists EventList in
your Java code to hold the master list of objects and point the EventListModel.source property in the YAML file to it.
Java:
public class GlazedListJListPanel extends JPanel {
private EventList<String> values = new BasicEventList<String>();
private BuildResult result;
public GlazedListJListPanel() {
result = SwingJavaBuilder.build(this);
}
}
YAML:
JPanel:
- JList(name=list):
EventListModel(name=model,source=values)
When you add/remove items in your EventList, they will automatically be propagated to the JList EventListModel.
51
7.1.2 JComboBox
EventComboBoxModel
The JComboBox-specific model in GlazedLists is called EventComboBoxModel. You will need to define a
GlazedLists EventList in your Java code to hold the master list of objects and point the EventComboBoxModel.source
property in the YAML file to it.
Java:
public class GlazedListJListPanel extends JPanel {
private EventList<String> values = new BasicEventList<String>();
private BuildResult result;
public GlazedListJListPanel() {
result = SwingJavaBuilder.build(this);
}
}
YAML:
JPanel:
- JComboBox(name=box):
EventComboBoxModel(name=model,source=values)
When you add/remove items in your EventList, they will automatically be propagated to the JComboBox EventComboBoxModel.
7.1.3 JTable
EventTableModel
The JTable-specific model in GlazedLists is called EventTableModel. You will need to define a GlazedLists EventList
in your Java code to hold the master list of objects and point the EventTableModel.source property in the YAML file
to it.
Java:
public class Person {
private String firstName;
private String lastName;
private Date birthDate;
//getters and setters for all properties...
}
public class GlazedListJListPanel extends JPanel {
private EventList<Person> values = new BasicEventList<Person>();
private BuildResult result;
public GlazedListJListPanel() {
result = SwingJavaBuilder.build(this);
}
}
YAML:
JPanel:
- JTable(name=list):
- EventTableModel(name=model,source=values)
When you add/remove items in your EventList, they will automatically be propagated to the JTable EventTableModel.
52
Chapter 7. Plugins
The example above will display only the firstName and lastName properties as columns. Alternatively, you can
to explicitly define the TableColumn instances:
JPanel:
- JTable(name=list):
- EventTableModel(name=model,source=values)
- TableColumn(name=firstName,headerValue=column.firstName)
- TableColumn(name=lastName,headerValue=column.lastName)
Since the headerValue column is localizable, it will automatically fetch the corresponding string key from the configured resource bundles.
Yet another option is to define all the columns you need via columns=[] and then just define the TableColumn
instance if you need to customize it further (e.g. add a cell renderer or editor).
Localizing column headers
When you define a list of columns in the columns=[] parameter, the builder will attempt to automatically look up
the header name for it using the following strategy:
1. look for a resource key equal to SimpleClassName.PropertyName, e.g. Person.firstName
2. look for a resource key equal to PropertyName, e.g. firstName
3. if no resource keys are found, attempt to build a header directly from the property name, e.g. firstName
becomes First Name
Handling duplicate column names
Within a single YAML file, all object names must be unique. So what happens if we have lets say two tables that
display data from different POJOs (or maybe different views of the same POJO) that happen to have the same names?
In this particular case, you can use the source property of the TableColumn level.
If this property is defined, the builder will use that instead of name to map a TableColumn to a POJOs property.:
JPanel:
- JTable(name=list1):
- EventTableModel(name=model,source=values)
- TableColumn(name=column1,source=firstName,headerValue=column.firstName)
- TableColumn(name=column2,source=lastName,headerValue=column.lastName)
- JTable(name=list2):
- EventTableModel(name=model,source=values)
- TableColumn(name=column3,source=firstName,headerValue=column.firstName)
- TableColumn(name=column4,source=lastName,headerValue=column.lastName)
In the sample above, two different tables display the same POJO data by using the optional source to avoid name
clashes on TableColumn.name.
Sorting
In order to enable sorting, you have to add the sort property, which has two allowed values:
53
In order to specify a pre-defined initial sort, pass in the list of column names into the sortBy property, e.g.:
EventTableModel(source=values,columns=[firstName,lastName,birthDate],
sort=multi,sortBy=[birthDate,lastName])
Note: The example above should a be a single line in your YAML file (its wrapped around here purely for display
purposes).
54
Chapter 7. Plugins
CHAPTER
EIGHT
and then you can start referring to it directly in the YAML file:
MyCustomClass(property1=value1,property2=value2, etc...)
You can also add it with a specific alias to avoid name collision (by default it takes the simple class name):
SwingJavaBuilder.getConfig().addType("CustomClassAlias",MyCustomClass.class);
CustomClassAlias(property1=value1,property2=value2, etc...)
ITypeHan-
If you need to inject some logic after a parents child nodes have been all processed, you need to implement the
ITypeHandlerFinishProcessor interface and add it to the appropriate TypeDefinition object.
56