Ease swing gui creation with html templating
Introduction
Creating GUI in java is rather difficult. Ordinary code looks like:
JPanel south = new JPanel(new BorderLayout()); south.add(new JLabel(«login»), BorderLayout.WEST); south.add(new JTextField(),BorderLayout.CENTER); rootPanel.add(south, BorderLayout.SOUTH);
Imagine that you have 15 fields...
TableLayout
and MigLayout
ease layout for many fields, but anyway you
have to write java code.
Background
A lot of programmers (especially in Russia) started with Delphi. The cool thing in Delphi is that you don't have to write GUI code at all. GUI markup is located in separate file and is linked to the app by IDE.
Almost the same approach is used in
Jetbrains IntelliJ Idea. The markup is located in .form
file and is
converted to the java code by IDE. Also form's (controller's) fields
are injected with appropriate components.
Using the code
My approach is to put all markup into external file and to construct form in runtime with the help of framework. This approach is like IOC pattern — you do not think about managing of form, you just describe it in declarative manner and use the results.
So here is example:
/ru/swing/html/example/LoginForm.html
<html> <head> <style type="text/css"> .red { color: red; } .top { vertical-align: top; } #loginForm { x-miglayout-column-constraints: [right]related[grow,fill]; } body { border: compound (empty 12 12 12 12) (compound (etched) (empty 12 12 12 12)); } </style> </head> <body id="rootPanel" style="display: border; width: 400; height:300; border: empty 12 12 12 12"> <table> <tr> <td rowspan="3" class="top"> <img src="/img/login_icon.gif" alt=""/> </td> <td width="fill"> <form id="loginForm"> <p>Login:</p> <input id="login" type="text" align="wrap"/> <p>Password:</p> <input id="password" type="password" align="wrap" /> </form> </td> </tr> <tr> <td> <p id="result" class="red"></p> </td> </tr> <tr> <td> <div style="display: box; x-boxlayout-direction: horizontal; border: empty 0 0 0 6"> <glue type="horizontal"/> <input type="button" text="OK" id="ok" icon="/img/accept.png" /> <strut type="horizontal" style="width: 6"/> <input type="button" text="Cancel" id="cancel" icon="/img/cancel.png"/> </div> </td> </tr> </table> </body> </html>
This is html markup of login form. Every html tag will produce some
swing component. For example, <body>
will produce
javax.swing.JPanel
.
'display'
attribute tells which layout
manager will be used in tag's component. 'border'
tell that it will
be java.awt.BorderLayout
.
Every direct child tag is placed in
parent's component. Most layout's (BorderLayout
, for example) place
component using 'align'
attribute as constraint. Default constraint
for BorderLayout
is center, so table will be placed in the center
part of JPanel
.
Also notice that tag <table>
is
used. It will produce JPanel, as <body>
, but will set
TableLayout as layout manager. After that it will specially handle
it's children, placing all <td>
contents in appropriate cell.
In simular manner, <form>
produces JPanel
with MigLayout
layout manager.
Now let's bind this form to the controller:
package ru.swing.html.example; import org.jdom.JDOMException; import ru.swing.html.Bind; import ru.swing.html.Binder; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; public class LoginForm { @Bind("login") private JTextField login; @Bind("password") private JPasswordField password; @Bind("ok") private JButton okBtn; @Bind("cancel") private JButton cancelBtn; @Bind("rootPanel") private JPanel rootPanel; @Bind("result") private JLabel result; public LoginForm() { try { Binder.bind(this); } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } init(); } public void init() { okBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { result.setText("Logging in user "+login.getText()); } }); cancelBtn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { result.setText("Cancel clicked"); } }); } public JPanel getRootPanel() { return rootPanel; } public static void main(String[] args) throws JDOMException, IOException { LoginForm loginForm = new LoginForm(); JFrame f = new JFrame("Test"); f.setSize(400, 200); f.getContentPane().add(loginForm.getRootPanel()); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setVisible(true); } }
From's markup is loaded and binded to controller using
try { Binder.bind(this); } catch (JDOMException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }
This call searches for file /ru/swing/html/example/LoginForm.html
(as class is named ru.swing.html.example.LoginForm
), loads it, builds
swing components tree and than binds is to controller, so components
who's tag's id is equal to the value of @Bind
annotation are injected
into the field.
The final result looks like:
Links
The code is located at http://code.google.com/p/swinghtmltemplate/
You can examine documentation at http://code.google.com/p/swinghtmltemplate/wiki/Documentation?wl=en
MigLayout http://www.miglayout.com/
TableLayout https://tablelayout.dev.java.net/
Post Comment
MRGI3n I cannot thank you enough for the blog post.Much thanks again. Awesome.