GroupPanel

class GroupPanel extends Panel

A panel that uses javax.swing.GroupLayout to visually arrange its components.

A panel that uses javax.swing.GroupLayout to visually arrange its components.

Note: This is a slightly adapted variant of the original GroupPanel class by Andreas Flier and which was part of the ScalaSwingContrib project. We thought it was a bit over-engineered, and also some naming was problematic (theHorizontalLayout is ...) and involving reflection-based structural types.

The key point to understanding this layout manager is that it separates horizontal and vertical layout. Thus, every component appears twice: once in the horizontal and once in the vertical layout. Consult the Java API documentation for GroupLayout and Sun's Java tutorials for a comprehensive explanation.

The main advantage of using this panel instead of manually tinkering with the layout is that this panel provides a concise, declarative syntax for laying out its components. This approach should make most use cases easier. In some special cases, e.g. when re-creating layouts on-the-fly, it might be preferable to use a more imperative style, for which direct access to the underlying layout manager is provided.

In contrast to the underlying swing layout, this panel activates the automatic creation of gaps between components by default, since this panel is intended for coding UIs "by hand", not so much for visual UI builder tools. Many features of the underlying layout are aimed at those, tough. Most of them are available through this panel for completeness' sake but it is anticipated that coders won't need to use them very much.

=Code examples=

This section contains a few simple examples to showcase the basic functionality of GroupPanels. For all examples, it is assumed that everything from the package scala.swing is imported and the code is placed inside a scala.swing.SimpleSwingApplication like this:

import scala.swing._
import de.sciss.swingplus._

object Example extends SimpleSwingApplication {
 lazy val top = new MainFrame {
   contents = new GroupPanel {
     // example code here
   }
 }
}

==Simple panel with 2 components==

In the first example, there's a label and a text field, which appear in a horizontal sequence but share the same vertical space.

val label     = new Label("Label:")
val textField = new TextField(20)

horizontal    = Seq(label, textField)
vertical      = Par(label, textField)

It can be observed that the resize behaviour of the text field is rather strange. To get better behaviour, the components' vertical sizes can be linked together.

linkVerticalSize(label, textField)

Alternatively, it would have been possible to disallow the resizing of the vertical, parallel group. To achieve this, the vertical layout line should be written this way:

vertical = Par(Leading, FixedSize)(label, textField)

Since text fields aren't resizable when used with baseline alignment (more about that further down), the following code also prevents (vertical) resizing:

vertical = Par(Baseline)(label, textField)

==Size and alignment==

Components can be added with custom size constraints (minimum, preferred, maximum size). The next example showcases that. The text field appears with a preferred height of 100 pixels and when the component is resized, it can be reduced to its minimum height of 50 pixels and enlarged to its maximum height of 200 pixels.

horizontal = Seq(label, textField)
vertical   = Par(label, Size(textField, 50, 100, 200))

The Size object holds some useful constants: Default, Preferred and Infinite, that can be used for any of the minimum, preferred and maximum arguments.

Instead of using these hints with Size.apply, one can also use the provided convenience methods Size.fixed and Size.fill.

Because the default alignment in a parallel group is Leading, both components are "glued" to the top of the container (panel). To align the label's text with the text inside the text field, an explicit alignment can be specified in a preceding argument list, like this:

horizontal = Seq(label, textField)
vertical   = Par(Baseline)(label, Size(textField, 50, 100, 200))

This example also shows a potential problem of baseline alignment: some components stop being resizable. More specifically, the javadoc for GroupLayout.ParallelGroup states:

  • Elements aligned to the baseline are resizable if they have have a baseline resize behavior of CONSTANT_ASCENT or CONSTANT_DESCENT.
  • Elements with a baseline resize behavior of OTHER or CENTER_OFFSET are not resizable.

Since a text field's resizing behaviour is CENTER_OFFSET, it is not resizable when used with baseline alignment.

==Gaps==

The GroupPanel turns on automatic creation of gaps between components and along the container edges. To see the difference, try turning this feature off manually by inserting the following lines:

autoGaps          = false
autoContainerGaps = false

With both types of gaps missing, the components are clamped together and to the container edges, which does not look very pleasing. Gaps can be added manually, too. The following example does this in order to get a result that looks similar to the version with automatically created gaps, albeit in a much more verbose manner.

horizontal = Seq(
 Gap.Container(),
 label,
 Gap.Preferred(Related),
 textField,
 Gap.Container()
)

vertical = Seq(
 Gap.Container(),
 Parallel(label, textField),
 Gap.Container()
)

Rigid gaps with custom size or completely manual gaps (specifying minimum, preferred and maximum size) between components are created with the Gap object:

bc.. horizontal = Seq(
 label,
 Gap(10, 20, 100),
 textField
)

vertical = Seq(
 Par(label, Gap(30), textField)
)

In a parallel group, such a gap can be used to specify a minimum amount of space taken by the group.

In addition to rigid gaps in the previous example, it is also possible to specify gaps that resize. This could be done by specifying a maximum size of Infinite. However, for the most commonly used type of these, there is a bit of syntax sugar available with the Spring and ContainerSpring methods.

bc.. horizontal = Seq(
 Gap.Container(),
 label,
 Gap.Spring(), // default is Related
 textField,
 Gap.ContainerSpring()
)

These create gaps that minimally are as wide as a Gap.Preferred would be - it is possible to specify whether the Related or Unrelated distance should be used - but can be resized to an arbitrary size.

bc.. horizontal = Seq(
 Gap.Container(),
 label,
 Gap.Spring(Unrelated),
 textField,
 Gap.ContainerSpring()
)

The preferred size can also be specified more closely (Size.Default or Size.Infinite aka "as large as possible"):

bc.. horizontal = Seq(
 Gap.Container(),
 label,
 Gap.Spring(Unrelated, Size.Infinite),
 textField,
 Gap.ContainerSpring(Size.Infinite)
)

Please note that Gap.Preferred, Spring, Gap.Container and Spring.Container may '''only''' be used inside a sequential group.

==A dialog with several components==

As a last, more sophisticated example, here's the GroupPanel version of the "Find" dialog presented as example for GroupLayout in the Java tutorials by Sun:

val label         = new Label("Find what:")
val textField     = new TextField
val caseCheckBox  = new CheckBox("Match case")
val wholeCheckBox = new CheckBox("Whole words")
val wrapCheckBox  = new CheckBox("Wrap around")
val backCheckBox  = new CheckBox("Search backwards")
val findButton    = new Button("Find")
val cancelButton  = new Button("Cancel")

horizontal = Seq(
 label,
 Par(
   textField,
   Seq(
     Par(caseCheckBox, wholeCheckBox),
     Par(wrapCheckBox, backCheckBox)
   )
 ),
 Par(findButton, cancelButton)
)

linkHorizontalSize(findButton, cancelButton)

vertical = Seq(
 Par(Baseline)(label, textField, findButton),
 Par(
   Seq(
     Par(Baseline)(caseCheckBox, wrapCheckBox),
     Par(Baseline)(wholeCheckBox, backCheckBox)
   ),
   cancelButton
 )
)

==Mapping component sequences==

Often you will want to build panels from a sequence of components and arrange them in a grid. The Seq.apply and Par.apply methods take a sequence of GroupPanel.Element instances, and therefore you may have to explicitly convert them, as the implicit conversion does not kick in for collections. There are two possibilities, as demonstrated in the last example:

class Param(val check: CheckBox, val label: Label, val slider: Slider, val index: Spinner)

val p1 = new Param(
 new CheckBox,
 new Label("Foo"),
 new Slider { value = 10 },
 new Spinner(new SpinnerNumberModel(10, 0, 100, 1))
)
val p2 = new Param(
 new CheckBox { selected = true },
 new Label("Bar"),
 new Slider,
 new Spinner(new SpinnerNumberModel(50, 0, 100, 1))
)
val params = List(p1, p2)

horizontal = Seq(
 Par(params.map(r => r.check: GroupPanel.Element): _*),
 Par(params.map(r => r.label: GroupPanel.Element): _*),
 new Par { params.foreach(r => contents += r.slider) },
 new Par { params.foreach(r => contents += r.index ) }
)

vertical = Seq(
 params.map { p =>
   Par(Center)(p.check, p.label, p.slider, p.index)
 }: _*
)

As can be seen, the Seq and Par classes can be instantiated and then populated through calls to the contents member.

Authors

Hanns Holger Rutz

Andreas Flierl

See also

javax.swing.GroupLayout

Companion
object
class Panel
trait Wrapper
trait Container
class Component
trait PeerContainer
trait UIElement
trait LazyPublisher
trait Publisher
trait Reactor
trait Proxy
class Object
trait Matchable
class Any

Type members

Classlikes

object Par
Companion
class
class Par(alignment: Alignment, resizable: Boolean) extends Group
Companion
object
object Seq
Companion
class
class Seq extends Group
Companion
object

Inherited classlikes

protected class Content
Inherited from
Wrapper
object keys
Inherited from
Component
object mouse

Contains publishers for various mouse events. They are separated for efficiency reasons.

Contains publishers for various mouse events. They are separated for efficiency reasons.

Inherited from
Component

Value members

Concrete methods

def autoContainerGaps: Boolean

Indicates whether gaps between components and the container borders are automatically created.

Indicates whether gaps between components and the container borders are automatically created.

def autoContainerGaps_=(value: Boolean): Unit

Sets whether gaps between components and the container borders are automatically created.

Sets whether gaps between components and the container borders are automatically created.

def autoGaps: Boolean

Indicates whether gaps between components are automatically created.

Indicates whether gaps between components are automatically created.

def autoGaps_=(value: Boolean): Unit

Sets whether gaps between components are automatically created.

Sets whether gaps between components are automatically created.

def honorVisibilityOf(comp: Component): Unit

The component will not take up any space when it's invisible (default).

The component will not take up any space when it's invisible (default).

def honorsVisibility: Boolean

Indicates whether the visibility of components is considered for the layout. If set to false, invisible components still take up space. Defaults to true.

Indicates whether the visibility of components is considered for the layout. If set to false, invisible components still take up space. Defaults to true.

def honorsVisibility_=(value: Boolean): Unit

Sets whether the visibility of components should be considered for the layout. If set to false, invisible components still take up space. Defaults to true.

Sets whether the visibility of components should be considered for the layout. If set to false, invisible components still take up space. Defaults to true.

def horizontal_=(value: Group): Unit
def ignoreVisibilityOf(comp: Component): Unit

The component will still take up its space even when invisible.

The component will still take up its space even when invisible.

def layoutStyle: LayoutStyle

Returns the layout style used.

Returns the layout style used.

def layoutStyle_=(value: LayoutStyle): Unit

Assigns a layout style to use.

Assigns a layout style to use.

def linkHorizontalSize(comps: Component*): Unit

Links the sizes of several components horizontally.

Links the sizes of several components horizontally.

Value Params
comps

the components to link

def linkSize(comps: Component*): Unit

Links the sizes (horizontal and vertical) of several components.

Links the sizes (horizontal and vertical) of several components.

Value Params
comps

the components to link

def linkVerticalSize(comps: Component*): Unit

Links the sizes of several components vertically.

Links the sizes of several components vertically.

Value Params
comps

the components to link

def replace(existing: Component, replacement: Component): Unit

Replaces one component with another. Useful for dynamic layouts.

Replaces one component with another. Useful for dynamic layouts.

Value Params
existing

the component to be replaced

replacement

the component replacing the existing one

def vertical_=(value: Group): Unit

Inherited methods

def background: Color
Inherited from
UIElement
def background_=(c: Color): Unit
Inherited from
UIElement
def border: Border
Inherited from
Component
def border_=(b: Border): Unit
Inherited from
Component
def bounds: Rectangle
Inherited from
UIElement
def componentOrientation: ComponentOrientation
Inherited from
UIElement
def componentOrientation_=(x: ComponentOrientation): Unit
Inherited from
UIElement
def contents: Seq[Component]
Inherited from
Wrapper
def cursor: Cursor
Inherited from
UIElement
def cursor_=(c: Cursor): Unit
Inherited from
UIElement
def deafTo(ps: Publisher*): Unit

Installed reaction won't receive events from the given publisher anylonger.

Installed reaction won't receive events from the given publisher anylonger.

Inherited from
Reactor
def displayable: Boolean
Inherited from
UIElement
def enabled: Boolean
Inherited from
Component
def enabled_=(b: Boolean): Unit
Inherited from
Component
override def equals(that: Any): Boolean
Definition Classes
Proxy -> Any
Inherited from
Proxy
def focusable: Boolean
Inherited from
Component
def focusable_=(b: Boolean): Unit
Inherited from
Component
def font: Font
Inherited from
UIElement
def font_=(f: Font): Unit
Inherited from
UIElement
def foreground: Color
Inherited from
UIElement
def foreground_=(c: Color): Unit
Inherited from
UIElement
def hasFocus: Boolean
Inherited from
Component
override def hashCode(): Int
Definition Classes
Proxy -> Any
Inherited from
Proxy
def ignoreRepaint: Boolean
Inherited from
UIElement
def ignoreRepaint_=(b: Boolean): Unit
Inherited from
UIElement
def inputVerifier: Component => Boolean
Inherited from
Component
def inputVerifier_=(v: Component => Boolean): Unit
Inherited from
Component
def listenTo(ps: Publisher*): Unit

Listen to the given publisher as long as deafTo isn't called for them.

Listen to the given publisher as long as deafTo isn't called for them.

Inherited from
Reactor
def locale: Locale
Inherited from
UIElement
def location: Point
Inherited from
UIElement
def locationOnScreen: Point
Inherited from
UIElement
def maximumSize: Dimension
Inherited from
UIElement
def maximumSize_=(x: Dimension): Unit
Inherited from
UIElement
def minimumSize: Dimension
Inherited from
UIElement
def minimumSize_=(x: Dimension): Unit
Inherited from
UIElement
def name: String
Inherited from
Component
def name_=(s: String): Unit
Inherited from
Component
def opaque: Boolean
Inherited from
Component
def opaque_=(b: Boolean): Unit
Inherited from
Component
def paint(g: Graphics2D): Unit
Inherited from
Component
def preferredSize: Dimension
Inherited from
UIElement
def preferredSize_=(x: Dimension): Unit
Inherited from
UIElement
def publish(e: Event): Unit

Notify all registered reactions.

Notify all registered reactions.

Inherited from
Publisher
def repaint(rect: Rectangle): Unit
Inherited from
UIElement
def repaint(): Unit
Inherited from
UIElement
def requestFocus(): Unit
Inherited from
Component
def requestFocusInWindow(): Boolean
Inherited from
Component
def revalidate(): Unit
Inherited from
Component
def self: Any
Inherited from
UIElement
def showing: Boolean
Inherited from
UIElement
def size: Dimension
Inherited from
UIElement
override def subscribe(listener: Reaction): Unit
Definition Classes
LazyPublisher -> Publisher
Inherited from
LazyPublisher
override def toString: String
Definition Classes
Component -> Proxy -> Any
Inherited from
Component
def toolkit: Toolkit
Inherited from
UIElement
def tooltip: String
Inherited from
Component
def tooltip_=(t: String): Unit
Inherited from
Component
override def unsubscribe(listener: Reaction): Unit
Definition Classes
LazyPublisher -> Publisher
Inherited from
LazyPublisher
def validate(): Unit
Inherited from
UIElement
def visible: Boolean
Inherited from
UIElement
def visible_=(b: Boolean): Unit
Inherited from
UIElement
def xLayoutAlignment: Double

Used by certain layout managers, e.g., BoxLayout or OverlayLayout to align components relative to each other.

Used by certain layout managers, e.g., BoxLayout or OverlayLayout to align components relative to each other.

Inherited from
Component
def xLayoutAlignment_=(x: Double): Unit
Inherited from
Component
def yLayoutAlignment: Double
Inherited from
Component
def yLayoutAlignment_=(y: Double): Unit
Inherited from
Component

Concrete fields

val layout: GroupLayout

This panel's underlying layout manager is a GroupLayout instance.

This panel's underlying layout manager is a GroupLayout instance.

Inherited fields

protected val _contents: Content
Inherited from
Wrapper
protected val listeners: RefSet[Reaction]
Inherited from
Publisher
lazy override val peer: JPanel
Inherited from
Panel
val reactions: Reactions

All reactions of this reactor.

All reactions of this reactor.

Inherited from
Reactor