One of the "Hello world" GUI applications I like to write is an ROT13 encoder. ROT13 is a (very) primitive encryption where each letter of the alphabet is rotated 13 places to the right and has the added benefit of being symmetrical, the decryption uses the same exact method. The application itself will have an input text field, a button and an output text field.
Note that this is still using Swing directly. There are ways of using WYSIWYG GUI editors with JRuby, but let's not get that involved quite yet.
In learning how to use Swing, one of the most important things to learn is layout managers. Lacking a WYSIWYG GUI layout tool, you'll be working with the layouts yourself. And while it's possible to do everything manually, it's much more feasible to give all your components (AKA "widgets," as they're called in most other toolkits) to a layout manager and have it figure out where to put everything.
The layout for this application will be simple. There are two text fields and a button. And.. well, that's it, so let's just lay them out vertically. To do this we'll use a "Flow" layout. The flow layout is something very close to a work processor. It knows the width of each component and the width of the frame it and will try to make "lines" of components, when it runs out of room it moves down and makes another line. In our case, we'll be using a frame about as wide as any of the components, so each component will be on a line of its own. For more information about layout managers, see A Visual Guide to Layout Managers.
The two components we'll need for this application are the JTextField and the JButton. The JTextField is a common single line text field. We'll be using it for both input and output, once you've instantiated a JTextField, you can access (read or write) the text inside using the text attribute.
Things get a bit messier, but also quite easy in Ruby, when talking about the JButton. A JButton usually has to have some way of carrying over the state of the application into a detached callback function in Java. In JRuby, the callback can be a closure. If you'll recall, a closure inherits the binding of the scope it was declared in. This means it has access to all local variables in the scope it was defined, and this can hold the state of the application (in this case, a reference to the two text fields) for us.
require 'java' import javax.swing.JFrame import javax.swing.JPanel import javax.swing.JButton import javax.swing.JTextField import java.awt.FlowLayout class String def rot13 tr 'A-Za-z','N-ZA-Mn-za-m' end end class MainWindow < JFrame def initialize super "ROT13" setDefaultCloseOperation JFrame::EXIT_ON_CLOSE panel = JPanel.new(FlowLayout.new(FlowLayout::CENTER)) input = JTextField.new(10) button = JButton.new("Encrypt") output = JTextField.new(10) panel.add input panel.add button panel.add output button.add_action_listener do|e| output.text = input.text.rot13 end add panel setSize 100, 130 setVisible true end end MainWindow.new
Things are more or less straightforward when you wrap your head around Swing (which is really pretty typical for a GUI toolkit). The top section first requires the java library, which gives us access to the import method. The import method is then used to bring access to a number of Java classes we'll need to use.
The rot13 method is a simple implementation using tr (short for "translate"). There are more straightforward methods, but this is easy to remember and implement, and only uses a single standard String method.
Onto the meat of the program. Since I need references to a number of the components later, I instantiate them all at once and assign them to local variables. Remember that these local variables act as the state for the callback closure. Next, I add them to the JPanel object (which is acting as a container for our layout manager).
Adding the callback for the JButton is simple using the add_action_listener method. It takes a single parameter called e here. We never use it, but it's an instant of ActionEvent. The only interesting event passed down by a JButton is the click event, so we don't need to worry about the actual event. The callback itself is straightforward.
And finally we add the panel to the frame, set the size, set the frame to visible (and hence all the components inside the frame) and on the very last line of the program, instantiate the frame.