codemash - building rich apps with groovy swingbuilder

41
@ Andres Almiray 2009 CodeMash 2009 Building Rich Applications with Groovy's SwingBuilder Andres Almiray

Upload: andres-almiray

Post on 17-May-2015

5.331 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Building Rich Applications with Groovy's SwingBuilder

Andres Almiray

Page 2: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Learn how to create a Swing application with Groovy using

the SwingBuilder

Goal

Page 3: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Agenda

SwingBuilder BasicsGetting up to SpeedAdvanced FeaturesCustom Components and ExtensionsBeyond Swing: Griffon

Page 4: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

What is Groovy?

Page 5: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

SwingBuilder Basics

Page 6: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Builder Basics

Builders are a form of a DSLFocused on Hierarchical Structures

SwingBuilder is not the only Builder in GroovyXMLBuilderAntBuilderObjectGraphBuilderStill Others…

Swing applications follow a hierarchical model: Window

PanelButton

Builders are a perfect fit for making Swing UIs

Page 7: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

A Quick HelloWorld Example

groovy.swing.SwingBuilder.build { frame( id: 'root', title:'Hello World', show:true, pack:true) { flowLayout() label('Enter a message:') textField(id:'name', columns:20) button('Click', actionPerformed: { ActionEvent e -> if (name.text) optionPane().showMessageDialog( root, name.text) }) }}

Page 8: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

What's going on?

Each node is syntactically a method callAll of these method calls are dynamically dispatched

Most don’t actually exist in bytecode

Child closures create hierarchical relationsChild widgets are added to parent containers

SwingBuilder node names are derived from Swing classes

Remove the leading ‘J’ from a Swing class when present

SwingBuilder also supports some AWT classes like layouts

Page 9: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Special nodes

containerembeds a component that accepts nesting

widgetembeds a component that does not accept nesting

beanbuilds any JavaBean class (not just components)

Page 10: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Getting up to Speed

Page 11: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Closures and Event Handlers

Closures can handle almost all of the Swing event choresAssignment to an event listener method is equivalent to adding a listener with that method

bean.actionPerformed = { … }in Groovy is the same in Java architecture asbean.addActionListener(new ActionListener() { void actionPerformed(ActionEvent evt) { … }})

Concise syntax, 23+ characters vs. 85+ characters

Closures are first class objectsVariables can hold ClosuresMember pointer operator turns methods into closures

closureInstance = object.&method

Page 12: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Closures and Event Handlers Example

class Controller { def handleFoo() { … } def handleBar(ItemSelectionEvent evt) { … }}def handleBif = {FocusEvent evt -> … }

swing.comboBox(actionPerformed: controllerInstance.&handleFoo, itemSelected: controllerInstance.&handleBar, focusGained: handleBif, focusLost: { FocusEvent evt -> … }componentHidden: { … }}

Page 13: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Actions

Action values are abstracted as attributes, easy to set

IconsText valuesKeyboard Accelerators

Closure in closure: is called when firing actionPreexisting actions can be passed in as node argument

No new action created, action values can be adjusted

actions() node works like a placeholder for all your actions

Child content is not added to parent widgets

Actions can and should be defined in one place

Page 14: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Actions Example: Definition

actions { … action( id: 'saveAction', name: 'Save', closure: controller.&fileSave, mnemonic: 'S', accelerator: shortcut('S'), smallIcon: imageIcon( resource:"icons/disk.png", class:Console), shortDescription: 'Save Groovy Script' ) …}

Page 15: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Actions Examples: Usage

menuBar { menu(text: 'File', mnemonic: 'F') { … menuItem(saveAction) // icon:null for MacOSX … }}

toolBar(rollover: true, constraints: NORTH) { … button(saveAction, text: null) …}

Page 16: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Look & Feel

SwingBuilder provides convenience node lookAndFeel() to change the global Look & FeelShorthand names with L&F sensible defaults

systemnimbusplasticxpsubstance

Themes, skins, fonts, and such can be configured Hooks exist to add other L&Fs with custom configurationsIt is recommended to set L&F before building any nodes.

Page 17: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Advanced Features

Page 18: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Threading and the Event Dispatch Thread

All painting and UI operations must be done in the EDT

Anything else should be outside the EDT

Swing has SwingUtilities using Runnable Java 6 technology adds SwingWorkerHowever, closures are Groovy

For code inside EDTedt { … }doLater { … }

For code outside EDTdoOutside { … }

Build UI on the EDTSwingBuilder.build { … }

Page 19: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Threading and the EDT Example

action( id: 'countdown', name: 'Start Countdown', closure: { evt -> int count = lengthSlider.value status.text = count while ( --count >= 0 ) { sleep( 1000 ) status.text = count } status.background = Color.RED })

Page 20: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Threading and the EDT Example

action( id: 'countdown', name: 'Start Countdown', closure: { evt -> int count = lengthSlider.value status.text = count doOutside { while ( --count >= 0 ) { sleep( 1000 ) edt { status.text = count } } doLater { status.background = Color.RED } } })

Page 21: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Binding

Binding a set of data to a view and back again is recurring problem found in desktop applicationsJava Specification Request-295 (JSR) does a good Job for the Java programming Language

Deals with static typing issues via genericsAccepts JavaServer Pages™ (JSP™) Expression Language to provide dynamic abilities

SwingBuilder implements a purely dynamic variant

SwingBuilder attributes provide an implicit object and propertyAdd bind() node to an attribute with source or targetAttribute hooks exist for validation, conversion, bind on demand

Page 22: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Binding Example

import groovy.swing.SwingBuilder

new SwingBuilder().edt { frame( title: "Binding Test", size: [200, 120], visible: true ) { gridLayout( cols: 1, rows: 2 ) textField( id: "t1" ) textField( id: "t2", editable: false ) } bind( source: t1, sourceProperty: "text", target: t2, targetProperty: "text" )}

Page 23: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Binding Example

import groovy.swing.SwingBuilder

new SwingBuilder().edt { frame( title: "Binding Test", size: [200, 120], visible: true ) { gridLayout( cols: 1, rows: 2 ) textField( id: "t1" ) textField( id: "t2", editable: false, text: bind(source: t1, sourceProperty: "text") ) }}

Page 24: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Binding Example

import groovy.swing.SwingBuilder

new SwingBuilder().edt { frame( title: "Binding Test", size: [200, 120], visible: true ) { gridLayout( cols: 1, rows: 2 ) textField( id: "t1" ) textField( id: "t2", editable: false, text: bind{ t1.text } ) }}

Page 25: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

@Bindable Annotation

New in Groovy 1.6 (now in beta)Uses the AST Transformation framework

@Bindable Annotates a Property or a ClassAnnotating a Class is the same as annotating all of the properties

AST is transformed to add Property Change support

add/removePropertyChangeListener methods added to Class

Unless already presentUses PropertyChangeSupport (unless already present)

Setter methods fire PropertyChangeEvents

Similar setup for @Vetoable and constrained properties

Page 26: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

@Bindable Annotation Example

class CountModelController {

@Bindable int count = 1

void incramentCount(def evt = null) { setCount(count + 1) }}

def model = new CountModelController()

…label(text:bind(source:model, sourceProperty:'count'))…

Page 27: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Custom Components and Extensions

Page 28: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Adding Arbitrary Components

Individual components may be added and used in the builder

Single use – pass-through widgetsWidget – for nodes with no child content

widget(new CoolComp(), opaque:false, … )Container – for nodes that can contain other widgets

container(new BigComp()) { … }

Multiple use – factory registrationSimple bean (no-args constructor, no fancy stuff)

registerBeanFactory('coolComp', CoolComp)Rich factory (provide your own node factory)

registerFactory('bigComp', BigCompFactory)

Page 29: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Suite Component Extensions

Extending SwingBuilder as a whole is a way to enhance SwingBuilder with suites of components

SwingXBuilderSwingX components, painters and effectsTimingFramework based animationsMany new JX variants of widgets override old valuesOld widgets available if classicSwing:true set in node attributeswithWorker() node -> threading with SwingWorker

JIDEBuilderProvides nodes for the Jide CL set of components Provides svg-enabled icon support

Page 30: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Suite Component Extensions

FlamingoBuilderFlamingo Suite of Swing componentsCommandButton, FlexiSlider, Svg icons (Ribbon support in the near future).Current release is based on Flamingo 4.0dev

MacWidgetsBuilderMacWidgets is a set of components that follow Apple's HIGCurrent build is based on MacWidgets 0.9.3.1

... and more to come

Page 31: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

SwingPad demo

Page 32: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

GraphicsBuilder

GraphicsBuilder is a builder for Java2D renderingSupports basic Java2D shapesAdds non-basic shapes and filters

Provides nodes for paints, filters, transformations, fonts, etc.

Almost all properties are observableAllows bind() and animate() to manipulate rendered items

Also renders to images and files via GraphicsRenderer

Page 33: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

GraphicsBuilder Example

def gr = new GraphicsRenderer()  gr.renderToFile("filters.png", 260, 210) {   antialias on     rect(x:40, y:40, width:180, height:130,  arcWidth:60, arcHeight:60, borderColor:false) {      linearGradient {        stop(offset:0,  color:'red')        stop(offset:0.5, color:'orange')        stop(offset:1,  color:'red')      }      filters(offset: 50) {        weave()        kaleidoscope(sides:10, angle:0,angle2:0)        lights()      }     }  }  

Page 34: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Griffon

Page 35: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Griffon's Goals

Simplify Swing development

Provide a common application layout

Minimize context gap between web application (Grails) and desktop application development with Groovy

Provide easier application deploymentstandalonewebstartapplet

Page 36: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Griffon

Demo

Page 37: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

For More Information

Groovyhttp://groovy.codehaus.org

Griffonhttp://griffon.codehaus.org

Builders mentioned in this presentationhttp://groovy.codehaus.org/Swing+Builderhttp://groovy.codehaus.org/SwingXBuilderhttp://griffon.codehaus.org/JideBuilderhttp://griffon.codehaus.org/FlamingoBuilderhttp://griffon.codehaus.org/MacWidgetsBuilderhttp://groovy.codehaus.org/GraphicsBuilder

Page 38: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

For More Information

Groovy Swing team blogshttp://jroller.com/aalmirayhttp://www.shemnon.com/spelinghttp://www.jameswilliams.be/blog

Twitter@aalmiray @shemnon @ecspike

Page 39: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

GroovyMag

http://groovymag.com

Page 40: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Q & A

Page 41: CodeMash - Building Rich Apps with Groovy SwingBuilder

@ Andres Almiray 2009 CodeMash 2009

Thank You!