James Williams
LinkedInMastodonGithub

Porting Simple Dialogs from Ruby Shoes to Griffon

Tags: Griffon Ruby

Sometimes porting a project to Griffon yields less code. Some might even argue that it does most of the time. Unfortunately, this isn't one of those cases. So you might be wondering why I still decided to blog about the port even though it doesn't paint Griffon in the best light. Despite the increase in lines of code, I thought the example exposed some aspects of Swing that people might not know about. First the obligatory screen shots and Ruby code:

Simple dialogs constructs a window with a set of buttons each exposing a different dialog box type. Similar to the Simple Timer example, our SimpleDialogsModel has a single variable for a fileChooser that we will reuse across four of our buttons.

SimpleDialogsModel.groovy

import groovy.beans.Bindable
import javax.swing.JFileChooser

class SimpleDialogsModel {
   @Bindable fileChooser = new JFileChooser()
}

Our view file is a little more complex this time but to make it more readable, I moved all actions over 2 lines long to the controller. We should be promoting proper MVC design anyways. FlowLayout tells the application to layout components next to each other and auto-wrap based on the dimensions of the frame. The results of interacting with the dialog boxes is populated in result label when a dialog is closed.

SimpleDialogsView.groovy

import java.awt.FlowLayout
import java.awt.Color
import javax.swing.JOptionPane
import static javax.swing.JFileChooser.*
import javax.swing.JColorChooser

application(title:'SimpleDialogs',
  size:[300,150],
  locationByPlatform:true,
  iconImage: imageIcon('/griffon-icon-48x48.png').image,
  iconImages: [imageIcon('/griffon-icon-48x48.png').image,
               imageIcon('/griffon-icon-32x32.png').image,
               imageIcon('/griffon-icon-16x16.png').image],
  layout:new FlowLayout()
) {
    button(text:'Ask', actionPerformed: {
        def result = JOptionPane.showInputDialog("What is your name?")
        view.result.text = "Your name is ${result}"
    })
    button(text:'Confirm', actionPerformed: {controller.confirm()})
    button(text:'Open File...', actionPerformed: {
        controller.openSaveFileOrDir('Open a File...', true, FILES_ONLY)
    })
    button(text:'Save File...',actionPerformed: {
        controller.openSaveFileOrDir('Open a File...', false, FILES_ONLY)
    })

    button(text:'Open Folder...', actionPerformed: {
        controller.openSaveFileOrDir('Open a Folder...', true, DIRECTORIES_ONLY)
    })
    button(text:'Save Folder...',actionPerformed: {
        controller.openSaveFileOrDir('Save a Folder...', false, DIRECTORIES_ONLY)
    })
    button(text:'Color', actionPerformed: {
        def color = JColorChooser.showDialog(null, "Pick a Color", Color.WHITE)
        view.result.text = "You selected R:${color.red},G:${color.green},B:${color.blue}"
    })
    label(id:'result')
}

JOptionPane is a great kitchen sink class for creating dialogs. It has presets for inputDialogs and we see in SimpleDialogsView but also confirm dialogs and message dialogs, some of which we will see later. Another great find is JColorChooser which shows a spiffy color selection tool that returns a java.awt.Color object when you select a color.

SimpleDialogsController.groovy

import javax.swing.JOptionPane
import static javax.swing.JOptionPane.*
import javax.swing.JFileChooser

class SimpleDialogsController {
    // these will be injected by Griffon
    def model
    def view

    void mvcGroupInit(Map args) {
        // this method is called after model and view are injected
    }

    def confirm = {
        def result = JOptionPane.showConfirmDialog(null,"Would you like to proceed?")
        switch(result) {
            case YES_OPTION:
                view.result.text = "You selected Yes."
                break
            case NO_OPTION:
                view.result.text = "You selected No."
                break
            case CANCEL_OPTION:
                view.result.text = "You selected Cancel."
                break
               }
    }


    def openSaveFileOrDir = { msg, openFile, selectionMode ->
        def result
        model.fileChooser.setDialogTitle(msg)
        model.fileChooser.setFileSelectionMode(selectionMode)
        if (openFile)
            result = model.fileChooser.showOpenDialog(null)
        else result = model.fileChooser.showSaveDialog(null)
        if (result == JFileChooser.APPROVE_OPTION) {
            def file = model.fileChooser.getSelectedFile()
            view.result.text = "You selected ${file.getName()}"
        }
    }
}

Our controller consists of two closures, a simple confirm closure to show a confirm dialog and respond to an answer and a slightly more complex closure to open or save a directory or file(no writes are done, it just reads the file object's name).

Though the Shoes example wins in terms of lines of code, one could argue that the Griffon example gives you more granularity and exposes code that Shoes has hidden. You can download the code here.