Publicidad
Publicidad

Más contenido relacionado

Publicidad
Publicidad

Introduction to Griffon

  1. Introduction to Griffon James Williams
  2. About Me ● Co-founder of Griffon ● Author of "Learning HTML5 Game Programming" http://amzn.to/HTML5-Game-Book ● Blog: http://jameswilliams.be ● Twitter: @ecspike
  3. 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
  4. 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!
  5. Common Griffon Commands ● create-app ● create-mvc ● test-app ● run-app ● help ● package ● Form: griffon <environment> command <options>
  6. 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.
  7. 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)
  8. Sample Griffon View File package hello application(title: 'Hello', preferredSize: [320, 240], pack: true, //location: [50,50], locationByPlatform:true, iconImage: imageIcon('/griffon-icon-48x48.png').image, iconImages: [/* truncated */]) { // add content here label('Content Goes Here') // delete me }
  9. Code cd <GRIFFON INSTALL DIR> cd samples/SwingPad griffon app
  10. Common Widgets ● label ● button ● checkBox, radioButton ● textField ● textArea ● panel ● hbox, vbox
  11. Code import javax.swing.JOptionPane button('Click', actionPerformed: { JOptionPane.showMessageDialog( null, "Button clicked at ${new Date()}" ) })
  12. Crash course in MigLayout ● Grid-based LayoutManager ● Human-readable ● Docking (BorderLayout) ● Absolute Positioning ● Constraints ● Units (px, mm, cm, pts, in, %)
  13. Crash course in MigLayout ● wrap, newline ● w/width, h/height ● Docking (BorderLayout) ● gap ● span ● cell [column] [row]
  14. Code import net.miginfocom.swing.MigLayout panel(layout:new MigLayout()) { label(text:'label') label(text:'Cell 1 1 ', constraints:'cell 1 1') }
  15. Code import net.miginfocom.swing.MigLayout panel(layout:new MigLayout()) { label(text:'Text', constraints:'wrap') label(text:'Text2 ') label(text:'Text3', constraints:'w 50px, newline') label(text:'Text4 ') textField(columns:10, constraints:'span 2, newline') }
  16. Griffon Models ● Hold data for the MVC triad ● Can send/receive(@Bindable) data to/from widgets in the view ● Can do Grails-style validation (plugin)
  17. Sample Griffon Model file import groovy.beans.Bindable class HelloModel { // @Bindable String propName }
  18. Binding Forms textField(text:bind{model.property}) textField(text:bind(source:model, sourceProperty:"data")) textField(text:bind(target:model, targetProperty:"data"))
  19. Griffon Controllers ● Brains of the MVC triad ● Contain actions for the triad ● Injected with references to the model and view
  20. 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 // }
  21. 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
  22. Code (From inside a Griffon app directory) griffon list-plugins griffon lP griffon plugin-info <name of plugin>
  23. Creating A ToDo Application
  24. Code griffon create-app TodoApp <wait... wait... wait...> griffon install-plugin swingx-builder griffon install-plugin swingxtras-builder griffon install-plugin validation
  25. Code (/src/main/todoapp/...) class TodoItem { String todoText Date dueDate List <String>tags Boolean isDone Boolean isSaved public tagsToString() { Arrays.sort(tags) tags.join(',') } }
  26. Code (/griffon-app/models/todoapp/.) import groovy.beans.Bindable class TodoModel { @Bindable String todoText @Bindable Date dueDate @Bindable tagsText List <TodoItem> todos }
  27. Code (griffon-app/views/todoapp/...) size: [640,480], locationByPlatform:true, layout: new MigLayout()) { // Add Todo field jxtextField(id:'todoField', columns:45, text:bind(target:model, 'todoText'), constraints:'w 90%') promptSupport(todoField, prompt: "Add Todo, type here") button(id:'btnAdd', text:'Add', constraints:'w 10%, wrap') }
  28. Code (griffon-app/views/todoapp/...) // More options (date, tags, etc) jxtaskPane(title:'More Options', constraints: 'w 100%, spanx 2, wrap', layout: new MigLayout()) { label(text:'Due Date:', constraints:'wrap') jxdatePicker(id:'datePicker', date:bind(target:model,'dueDate'), constraints:'wrap') label(text:'Tags', constraints:'wrap') textField(id:'tagsField', columns:40, text:bind(target:model, 'tagsText')) promptSupport(tagsField, prompt:'Enter tags comma separated') }
  29. Swing isn't slow, ...
  30. ... devs are just coding it badly
  31. Don't do this! JButton b = new JButton("Run query"); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { runQueries(); } });
  32. 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
  33. (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)
  34. Code (griffon-app/controllers/...) def load = { doOutside { def todos = model.derby.all() todos.each { def todo = TodoItem.fromMap(it) model.todos.add(todo) } } }
  35. Adding a table of todo items
  36. The old way ... ● A very brittle TableModel ● Hard to update and sort items ● Hard to map between POJOs and rows
  37. Code (SwingPad) import javax.swing.table.DefaultTableModel def a = new DefaultTableModel(['A','B','C'] as String[], 0) a.addRow(1,2,3) a.addRow(4,5,6) a.addRow(7,8,9) scrollPane { jxtable(model:a) }
  38. Code (griffon-app/views/todoapp/...) ... jxtitledPanel(title:'Tasks') { scrollPane { jxtable(id:'table', model:model.todoTableModel) } }
  39. 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
  40. 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
  41. 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
  42. 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)
  43. 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)
  44. 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)
  45. Adding a bit of Swing glue: Table Editors and Renderers
  46. 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
  47. 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
  48. 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 } }
  49. 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() } }
  50. 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())
  51. 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)
  52. Code (griffon-app/controllers/...) def deleteCompleted = { def lock = model.todos.getReadWriteLock().writeLock() lock.lock() def list = model.todos.findAll{item -> item.isDone == true } list.each { item -> model.derby.remove(item.id) model.todos.remove(item) } lock.unlock() }
  53. Saving to disk
  54. 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
  55. 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
  56. 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
  57. 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"} )
  58. Validation
  59. 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
  60. Code (griffon-app/models/...) package todo import jwill.deckchair.* import groovy.beans.Bindable //... @Validatable class TodoModel { @Bindable String todoText @Bindable Date dueDate @Bindable tagsText //... static constraints = { todoText(blank:false) dueDate(nullable:true) tagsText(blank:true) } }
  61. Supported Validators ● blank ● min ● creditCard ● minSize ● email ● notEqual ● inetAddress ● nullable ● inList ● range ● matches ● size ● max ● url
  62. Validation an object def addItem = { if (model.validate()) { // save file } catch(Exception ex) { } } else { model.errors.each{error -> println error } } }
  63. Showing a error component jxtextField( id:'todoField', columns:45, text:bind(target:model, 'todoText'), constraints:'w 90%', errorRenderer:'for: todoText, styles: [highlight, popup]' )
  64. Printing in Griffon
  65. There once was a java.net project called EasyPrint
  66. It was a casualty to the Java. net/Kenai conversion :(
  67. From HTML to PDF
  68. Code (griffon-app/controllers/...) def builder = new MarkupBuilder(writer) builder.html { head{ title('My Todo List') } body { h2("My Todo List") br {} table (width:'90%', border:1, 'border-spacing':0){ tr { td('Completed'); td('Description'); td('Due Date'); td('Tags') } model.todos.each { item -> tr { if (item.isDone) { td('Done') } else { td() } td(item.todoText) td(item.dueDate) td(item.tagsToString()) } } } } }
  69. Code (griffon-app/controllers/...) def createPDF = { createHTML() def file = File.createTempFile("todos","txt"); file.write(writer.toString()) def docBuilder = DocumentBuilderFactory.newInstance(). newDocumentBuilder() def doc = docBuilder.parse(file) def renderer = new ITextRenderer() renderer.setDocument(doc, null) def outputFile = "todos.pdf" def os = new FileOutputStream(outputFile) renderer.layout(); renderer.createPDF(os); os.close() edt { JOptionPane.showMessageDialog(null, "PDF created.") } }
  70. Questions ?
Publicidad