Skip to content
daveray edited this page Jul 23, 2011 · 13 revisions

Seesaw has a bunch of functions for constructing "widgets". Widgets are user interface components that are displayed on the screen. They're called widgets for no good reason other than the name is shorter than "component" and it might help people forget that Swing lives under Seesaw :)

That said, a Seesaw widget is just a Swing component with some additional metadata attached to make things like Selectors work and to generally make them more pleasant to work with. In fact, most Seesaw functions that take a "widget" as an argument can take any old Swing component as well.

Widget Construction with MakeWidget

Note that (seesaw.core/make-widget) is usually called automatically by Seesaw when appropriate so you rarely need to call it yourself.

Seesaw includes a protocol (MakeWidget) for constructing widgets from arbitrary objects. This is usually invoked on the values of the :items and other "member" options for containers. It allows for a hopefully more natural UI construction process. For example, to make a label ((label) in Seesaw, or just JLabel in Swing), just pass a String:

(vertical-panel :items ["This" "is" "a" "vertical" "stack of" "JLabels"])

The following table outlines the widget coercions that are provided in Seesaw out of the box. If you feel like it, you can provide coercions for your own types. See the [make-widget] (https://github.com/daveray/seesaw/blob/master/src/seesaw/examples/make_widget.clj) example for details. See (seesaw.core/make-widget).

Without further ado, here are the coercions:

Input Result
java.awt.Component return argument unchanged
java.util.EventObject (for example in an event handler) return the event source
java.awt.Dimension return Box/createRigidArea
java.swing.Action return a button using the action
:fill-h Box/createHorizontalGlue
:fill-v Box/createVerticalGlue
[:fill-h n], e.g. [:fill-h 99] Box/createHorizontalStrut with width n
[:fill-v n] Box/createVerticalStrut with height n
[width :by height] create rigid area with given dimensions
A URL a label with the image located at the url
A non-url string a label with the given text

Most of Seesaw's container functions (flow-panel, grid-panel, etc) take an :items property which is a list of these widget-able values. For example:

(let [choose (fn [e] (alert "I should open a file chooser"))]
  (flow-panel
    :items ["File"                                 [:fill-h 5] 
            (text (System/getProperty "user.dir")) [:fill-h 5] 
            (action :handler choose :name "...")]))

creates a panel with a "File" label, a text entry field initialized to the current working directory and a button that doesn't do much. Each component is separated by 5 pixel padding.

New coercions can be added by extending the MakeWidget protocol. See the make-widget example.

Widget Conversion with ToWidget

Note that (seesaw.core/to-widget) is usually called automatically by Seesaw when appropriate so you rarely need to call it yourself.

Additionally, there is a protocol, (ToWidget), which performs conversion of objects to existing widgets, i.e. no new widgets are constructed. This is used in most cases where you have something that indirectly refers to a widget (like an event object) and you'd like to pass it to widget manipulation functions as if it were the widget itself. Out of the box, the following conversions are provided:

Input Result
nil nil
java.awt.Component return argument unchanged
java.util.EventObject (for example in an event handler) return the event source

New conversions can be added by extending the ToWidget protocol.

Default Properties

All of Seesaw's widget creation functions (label, text, horizontal-panel, etc) support a base set of properties:

Property Description
:id A unique id for the widget for use with `(select)` (see below).
:class A logical class (or set of classes) for the widget for use with `(select)` (see below). Equivalent to the `class` attribute in html.
:opaque? (boolean) Set whether the background of the widget is opaque.
:focusable? (boolean) Set whether the widget can receive keyboard focus.
:background Background color by coercing into a Color (see below)
:foreground Foreground color by coercing into a Color (see below)
:border Set the border of the widget by coercing into a Border. See below.
:font Set the font of the widget by coercing into a Font. See below.
:enabled? Whether the widget is enabled or not.
:minimum-size Minimum size of component, set with a `java.awt.Dimension` or a vector of the form `[width :by height]`, for example `[50 :by 50]`. Note that in Swing, some containers don't honor minimum size.
:maximum-size Same as `:minimum-size`, but maximum. Note that in Swing, some containers don't honor maximum size.
:preferred-size Same as `:minimum-size`, but preferred size.
:size Set `:minimum-size`, `:maximum-size`, and `:preferred-size` all at once.
:location Set the location of the widget with a `[x y]` vector or a `java.awt.Point`. _Note this is only useful when there is no layout on the container._
:bounds Set the bounds (location and size) of the widget with a `[x y width height]` vector or a `java.awt.Rectangle`. _Note this is only useful when there is no layout on the container._
:listen List of event listeners with same format as args to `(listen)` function (see below).
:popup A JPopupMenu or function that generates the items of a popup menu to display. In this case, Seesaw ensures that the popup is shown on the correct mouse event for the platform. See `src/seesaw/examples/popup.clj`

... and many more. See code and tests for details.

Note that these properties can also generally be manipulated with the (config!) function which applies them to an existing widget or widgets:

(config! (select root [:#my-widget]) :enabled? false :text "I'm disabled.")

(config!) can be applied to a single widget, or list of widgets, or things that can be turned into widgets.

Scrolling

Use the (scrollable) function to make a widget scrollable:

(scrollable (text :multi-line? true))

Note that this returns a JScrollPane object, not the widget passed in.

Splitters

Use the (top-bottom-split) or (left-right-split) functions to make a splitter each takes two widget args:

(top-bottom-split "Top" "Bottom")
(left-right-split "Top" "Bottom")

Seesaw has some basic support for Java2D drawing, using the (canvas) function to create a paintable panel. See src/seesaw/graphics.clj and src/seesaw/examples/canvas.clj.

Integrating Custom Widgets

Note that though sub-classing is very common in Swing tutorials, it's actually often not as necessary as you'd think.

If you're using custom widgets, either sub-classes of your own, or from another toolkit like SwingX, you can still take advantage of Seesaw, even for constructing the widgets. The `(seesaw.core/with-widget)' macro let's you use a custom sub-class with an existing Seesaw widget construction function. For example:

(with-widget org.jdesktop.swingx.JXList
    (listbox :id :my-jxlist ... other listbox options ...))

This will return JXList rather than just JList. If the given class isn't a sub-class of whatever the construction function creates, IllegalArgumentException is thrown.

See the docs for the (seesaw.core/with-widget) macro for more info.

Clone this wiki locally