A JGoodies FormLayout example (Sarah3 input panel)

If you ever wanted to see how easy it is to create a form using the JGoodies FormLayout, this code shows a simple example:

/**
 *  the "input panel" that goes in the North section of the Sarah3 mainframe BorderLayout.
 */
private def buildInputPanel: JPanel = {
 
    val layout = new FormLayout(
            //    label       textfield
            //    -----       ---------
            "3px, 48px,  8px, pref:grow, 2dlu",  //columns
            //    -----       ---------
            "3dlu, p, 3dlu"                                          //rows
    )
    val builder = new PanelBuilder(layout)
    builder.setDefaultDialogBorder

    val cc = new CellConstraints   
    builder.add(statusLabel, cc.xy(2,2))
    builder.add(inputField,  cc.xy(4,2))

    builder.getPanel

}

That code builds the JPanel that’s shown at the top of this image, including the traffic light and wide textfield:

I describe more about how to use the JGoodies form layout PanelBuilder in this tutorial, “Getting Java Swing components to grow and fill using JGoodies FormLayout.

As one word of caution, this code is based on an old version of the JGoodies FormLayout class. I haven’t learned their newer classes, and I don’t know if this will work with their latest releases.

The complete source code

If it helps to see that panel-building in the context of the complete class, here’s the source code for the current Sarah3 JFrame:

package com.devdaily.sarah.gui

import javax.swing._
import java.awt._
import com.jgoodies.forms.layout.FormLayout
import com.jgoodies.forms.builder.PanelBuilder
import com.jgoodies.forms.layout.CellConstraints

trait StatusIndicator
case object RedLight extends StatusIndicator
case object YellowLight extends StatusIndicator
case object GreenLight extends StatusIndicator
case object NeutralLight extends StatusIndicator

class MainFrame3 extends JFrame {
  
    import SwingUtils.invokeLater
  
    // status icon images 
    val trafficLightRed = new ImageIcon(classOf[MainFrame3].getResource("trafficLightRedSmall.jpg"))
    val trafficLightYellow = new ImageIcon(classOf[MainFrame3].getResource("trafficLightYellowSmall.jpg"))
    val trafficLightGreen = new ImageIcon(classOf[MainFrame3].getResource("trafficLightGreenSmall.jpg"))
    val trafficLightNeutral = new ImageIcon(classOf[MainFrame3].getResource("trafficLightNeutralSmall.jpg"))
    
    setLayout(new BorderLayout)

    // input area
    val statusLabel = new JLabel
    val inputField = new JTextField
    statusLabel.setIcon(trafficLightNeutral)
    val inputPanel = buildInputPanel
    
    // output area
    val outputArea = new JEditorPane
    val scrollPane = new JScrollPane(outputArea)
    
    configureInputField
    configureOutputArea
    
    getContentPane.add(inputPanel, BorderLayout.NORTH)
    getContentPane.add(scrollPane, BorderLayout.CENTER)
    
    def setStatusIndicator(status: StatusIndicator): Unit = status match {
        case RedLight => invokeLater(statusLabel.setIcon(trafficLightRed))
        case YellowLight => invokeLater(statusLabel.setIcon(trafficLightYellow))
        case GreenLight => invokeLater(statusLabel.setIcon(trafficLightGreen))
        case NeutralLight => invokeLater(statusLabel.setIcon(trafficLightNeutral))
    }
    
    /** 
     *  the "input panel" that goes in the North section of the Sarah3 mainframe BorderLayout.
     */
    private def buildInputPanel: JPanel = {
      
        val layout = new FormLayout(
                //    label       textfield
                //    -----       ---------
                "3px, 48px,  8px, pref:grow, 2dlu",  //columns
                //    -----       ---------
                "3dlu, p, 3dlu"                      //rows
        )
        val builder = new PanelBuilder(layout)
        builder.setDefaultDialogBorder
    
        val cc = new CellConstraints    
        builder.add(statusLabel, cc.xy(2, 2))
        builder.add(inputField,  cc.xy(4, 2))

        builder.getPanel
    
    }

    private def configureOutputArea {
        outputArea.setEditable(false)
        outputArea.setFont(inputField.getFont.deriveFont(20.0f))
        outputArea.setBackground(new Color(230, 230, 230))
        outputArea.setMargin(new Insets(20, 20, 20, 20))
        outputArea.setText("Welcome to Sarah III")
    }

    private def configureInputField {
        inputField.setFont(inputField.getFont.deriveFont(24.0f));
        inputField.setBorder(BorderFactory.createCompoundBorder(inputField.getBorder, BorderFactory.createEmptyBorder(5, 12, 5, 5)))
        inputField.setBounds(20, 20, inputField.getHeight, inputField.getWidth)
        inputField.setBackground(new Color(250, 250, 250))
    }
    
    def setFocusInTextField {
        val mainFrame = this
        invokeLater(new Runnable {
            def run {
                mainFrame.requestFocusInWindow
                inputField.requestFocusInWindow
                Thread.sleep(500)
            }
        })
    }
}

I don’t have time to explain that code, but I hope it makes sense when you can see it along with the JFrame image I shared above.