About Me
● Co-founder of Griffon
● Author of "Learning HTML5
Game Programming"
http://amzn.to/HTML5-Game-Book
● Blog: http://jameswilliams.be
● Twitter: @ecspike
Agenda
● What is Griffon?
● Common Griffon Commands
● Model - View - Controller
● MigLayout
● Binding
● Plugins
● Validation
● Lots of code
Code for this demo: https://github.com/jwill/Conference-Demos
What is Griffon?
● Apache 2 Licensed
● http://griffon.codehaus.org
● Inspired by Grails and the Swing Application Framework
● Extensible with plugins and addons
● Deployable to Webstart, Applet, or single jar file
● Now to Github!
Griffon Aliases
● create-app
● create-mvc => cM
● test-app => tA
● run-app => app
● help => h
● package => p
You can even create your own with the create-alias command.
Griffon Views
● Represent the presentation layer of the MVC triad
● Use Domain Specific Languages called UI Builders
● SwingBuilder is bundled
● Additional builder available as plugins (SwingXBuilder,
JIDE, MacWidgets, etc)
Crash course in MigLayout
● Grid-based LayoutManager
● Human-readable
● Docking (BorderLayout)
● Absolute Positioning
● Constraints
● Units (px, mm, cm, pts, in, %)
Griffon Controllers
● Brains of the MVC triad
● Contain actions for the triad
● Injected with references to the model and view
Sample Griffon Controller File
class HelloController {
// these will be injected by Griffon
def model
def view
// void mvcGroupInit(Map args) {
// // this method is called after model and view are injected
// }
// void mvcGroupDestroy() {
// // this method is called when the group is destroyed
// }
Griffon Plugins
● Extend app functionality at runtime or compile-time
● Can be as simple as adding a couple jars...
● Or as complex as creating a custom builder to use them
● Common areas:
○ UI Toolkits
○ Dependency Injection
○ Persistence
Code
(From inside a Griffon app directory)
griffon list-plugins
griffon lP
griffon plugin-info <name of plugin>
Don't do this!
JButton b = new JButton("Run query");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
runQueries();
}
});
Threading in Swing
● All updates to the UI must happen on the single Event
Dispatcher Thread(EDT)
● Nothing else should be executed on the EDT*
● Bad dev starts long running task, UI locks up until it is done
● SwingUtilities provides the following functions:
○ isEventDispatchThread()
○ invokeLater(Runnable),
invoke Runnable when convenient
○ invokeAndWait(Runnable)
invoke when convenient and wait for completion
(Swing) Threading in Griffon
● Encapsulates SwingUtilities and simple threading
● Adds closure for running a task outside the EDT
● Available closures:
○ doLater { ... } ==> SwingUtilities.doLater
○ edt{ ...} ==> SwingUtilities.invokeAndWait
○ doOutside{..} ==> Spawns new Thread if inside EDT
● Griffon 0.9.2+ encloses controller actions with doOutside by
default (can be turned off)
GlazedLists
● Easy data model management for:
○ JComboBox,
○ JList
○ JTable
● Provides easy dynamic sorting and filtering
● Supports both SWT and Swing
● Supports concurrency
● Link: http://glazedlists.com
BasicEventList
● Compatible with ArrayList and Vector
● Stores the items for your List or Table
● Parameterized around POJO
BasicEventList todos = new BasicEventList<TodoItem>()
● Adds listeners for changes in the list
BasicEvent<Widget>Model
● Receives changes from BasicEventList and pushes them to
the widget
● BasicEventListModel and BasicEventComboModel take
only a BasicEventList (POJOs to display) as a parameter
● JTables require a bit more definition
TableFormat
● Interface that defines how the table is displayed in the GUI
● By default is not editable after initialization
● Required functions:
○ getColumnCount()
○ getColumnName(int)
○ getColumnValue(obj, int)
WritableTableFormat
● Interface allowing editing of fields
● Can set granular edit policy with isEditable
● Uses default field editor (usually a JTextField)
● Required functions:
○ isEditable(obj, column)
○ setColumnValue(obj, value, column)
AdvancedTableFormat
● Interface that allows custom cell renderers
For example: DatePickers, Check boxes, Sliders, etc.
● JTables set policies on how to render a cell with a certain
class type
● JXTables do sorting and filtering so a separate comparator
is not needed
● Required functions:
○ getColumnClass(int)
○ getColumnComparator(int)
Adding a bit of Swing glue:
Table Editors and Renderers
TableEditor vs TableRenderers
● Any Swing component can be an editor or renderer
● TableEditors show when that cell is being edited
● TableRenderers appear in all other cases
● Can be mixed an matched
For example: a default renderer but a specialized renderer
Default TableRenderers
Class Renderer Component
Boolean* JCheckBox*
Number JLabel, right-aligned
Double, Float JLabel, right-aligned with
coersion to String using
NumberFormat
Date JLabel, with coersion to String
using DateFormat
Object JLabel with object's toString()
value
Code (src/main/groovy)
class CheckBoxRenderer extends JCheckBox implements TableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object
value, boolean isSelected, boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground())
setBackground(table.getSelectionBackground())
} else {
setForeground(table.getForeground())
setBackground(table.getBackground())
}
setSelected(value)
return this
}
}
Code (src/main/groovy)
class DatePickerEditor extends AbstractCellEditor implements TableCellEditor {
def component = new JXDatePicker()
public Component getTableCellEditorComponent(JTable table, Object value,
boolean isSelected, int row, int column) {
component.setDate(value)
return component
}
public Object getCellEditorValue() {
return component.getDate()
}
}
Code (griffon-app/controllers/...)
// setup renderers and editors
view.table.setDefaultRenderer(Boolean.class, new CheckBoxRenderer())
view.table.setDefaultEditor(Boolean.class, new DefaultCellEditor(new
JCheckBox()))
view.table.setDefaultEditor(Date.class, new DatePickerEditor())
Code (griffon-app/controllers/...)
// table model listener
model.todoTableModel.addTableModelListener([tableChanged:
{evt ->
def i = evt.getFirstRow()
def j = evt.getLastRow()
if (i == j && evt.getType() == TableModelEvent.UPDATE) {
// do something with the update date
}
}] as TableModelListener)
Introducing Deckchair
● Modeled after Lawnchair, a lightweight JSON store (JS)
● Written in Groovy
● Uses an adapter-implementation model for persistence
Derby is the only implemented backend for Deckchair
● Provides more flexibility than direct use of backends
● Link: http://github.com/jwill/deckchair
Deckchair API
● save, saves a single object
● get, retreives an object
● each, executes code for every item in the result set
● find, finds objects using a closure
● all, returns all objects in the set
● nuke, destroys all objects in the set
● remove, destroys a single object
Deckchair Derby Internals
● Creates a table with:
○ id, 36-character VARCHAR
○ timestamp, BIGINT
○ value, LONG VARCHAR (about 32,000 characters)
● Objects are serialized to JSON strings on insertion
● Only the uniqueness of the id field is enforced
● Good for small amounts of data and demos
● Good for data models that aren't exactly pinned down yet
Code
// from model
def derby = new Deckchair([name:'todos', adaptor:'derby'])
//loading todos
def todos = model.derby.all()
// save function with optional closure to print status after completion
model.derby.save(changedTodo.toMap(),
{obj -> println obj; println "saved todo"}
)
GValidation Plugin
● Provides Grails style validation and messages
● Uses a Grails-like DSL
● Supports custom constraints in addition to built-in types
● Provides visual cues for incorrect data
● Can internationalize error messages with the Griffon i18n
plugin