class GroupPanel extends Panel
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_ASCENTorCONSTANT_DESCENT. - Elements with a baseline resize behavior of
OTHERorCENTER_OFFSETare 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.
- See also
javax.swing.GroupLayout
- Alphabetic
- By Inheritance
- GroupPanel
- Panel
- Wrapper
- Container
- Component
- UIElement
- LazyPublisher
- Publisher
- Reactor
- Proxy
- AnyRef
- Any
- Hide All
- Show All
- Public
- All
Instance Constructors
- new GroupPanel()
Type Members
Value Members
-
final
def
!=(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
final
def
##(): Int
- Definition Classes
- AnyRef → Any
-
final
def
==(arg0: Any): Boolean
- Definition Classes
- AnyRef → Any
-
def
Baseline: Alignment
- Attributes
- protected
-
def
Center: Alignment
- Attributes
- protected
-
def
Gap: GroupPanel.Gap.type
- Attributes
- protected
-
def
Indent: ComponentPlacement
- Attributes
- protected
-
def
Leading: Alignment
- Attributes
- protected
-
def
Related: ComponentPlacement
- Attributes
- protected
-
def
Size: GroupPanel.Size.type
- Attributes
- protected
-
def
Trailing: Alignment
- Attributes
- protected
-
def
Unrelated: ComponentPlacement
- Attributes
- protected
-
val
_contents: Content
- Attributes
- protected
- Definition Classes
- Wrapper
-
final
def
asInstanceOf[T0]: T0
- Definition Classes
- Any
-
def
autoContainerGaps: Boolean
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.
-
def
autoGaps: Boolean
Indicates whether gaps between components are automatically created.
-
def
autoGaps_=(value: Boolean): Unit
Sets whether gaps between components are automatically created.
-
def
background: Color
- Definition Classes
- UIElement
-
def
background_=(c: Color): Unit
- Definition Classes
- UIElement
-
def
border: Border
- Definition Classes
- Component
-
def
border_=(b: Border): Unit
- Definition Classes
- Component
-
def
bounds: Rectangle
- Definition Classes
- UIElement
-
def
clone(): AnyRef
- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
def
componentOrientation: ComponentOrientation
- Definition Classes
- UIElement
-
def
componentOrientation_=(x: ComponentOrientation): Unit
- Definition Classes
- UIElement
-
def
contents: scala.Seq[Component]
- Definition Classes
- Wrapper → Container
-
def
cursor: Cursor
- Definition Classes
- UIElement
-
def
cursor_=(c: Cursor): Unit
- Definition Classes
- UIElement
-
def
deafTo(ps: Publisher*): Unit
- Definition Classes
- Reactor
-
def
displayable: Boolean
- Definition Classes
- UIElement
-
def
enabled: Boolean
- Definition Classes
- Component
-
def
enabled_=(b: Boolean): Unit
- Definition Classes
- Component
-
final
def
eq(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
def
equals(that: Any): Boolean
- Definition Classes
- Proxy → Any
-
def
finalize(): Unit
- Attributes
- protected[java.lang]
- Definition Classes
- AnyRef
- Annotations
- @throws( classOf[java.lang.Throwable] )
-
def
focusable: Boolean
- Definition Classes
- Component
-
def
focusable_=(b: Boolean): Unit
- Definition Classes
- Component
-
def
font: Font
- Definition Classes
- UIElement
-
def
font_=(f: Font): Unit
- Definition Classes
- UIElement
-
def
foreground: Color
- Definition Classes
- UIElement
-
def
foreground_=(c: Color): Unit
- Definition Classes
- UIElement
-
final
def
getClass(): Class[_]
- Definition Classes
- AnyRef → Any
-
def
hasFocus: Boolean
- Definition Classes
- Component
-
def
hashCode(): Int
- Definition Classes
- Proxy → Any
-
def
honorVisibilityOf(comp: Component): Unit
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.
Indicates whether the visibility of components is considered for the layout. If set to
false, invisible components still take up space. Defaults totrue. -
def
honorsVisibility_=(value: Boolean): Unit
Sets whether the visibility of components should be considered for the layout.
Sets whether the visibility of components should be considered for the layout. If set to
false, invisible components still take up space. Defaults totrue. - def horizontal: Group
- def horizontal_=(value: Group): Unit
-
def
ignoreRepaint: Boolean
- Definition Classes
- UIElement
-
def
ignoreRepaint_=(b: Boolean): Unit
- Definition Classes
- UIElement
-
def
ignoreVisibilityOf(comp: Component): Unit
The component will still take up its space even when invisible.
-
var
initP: JComponent
- Definition Classes
- Component
-
def
inputVerifier: (Component) ⇒ Boolean
- Definition Classes
- Component
-
def
inputVerifier_=(v: (Component) ⇒ Boolean): Unit
- Definition Classes
- Component
-
final
def
isInstanceOf[T0]: Boolean
- Definition Classes
- Any
-
val
layout: GroupLayout
This panel's underlying layout manager is a
GroupLayoutinstance. -
def
layoutStyle: LayoutStyle
Returns the layout style used.
-
def
layoutStyle_=(value: LayoutStyle): Unit
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.
- 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.
- comps
the components to link
-
def
linkVerticalSize(comps: Component*): Unit
Links the sizes of several components vertically.
Links the sizes of several components vertically.
- comps
the components to link
-
def
listenTo(ps: Publisher*): Unit
- Definition Classes
- Reactor
-
val
listeners: RefSet[Reaction] { val underlying: scala.collection.mutable.HashSet[scala.ref.Reference[scala.swing.Reactions.Reaction]] }
- Attributes
- protected
- Definition Classes
- Publisher
-
def
locale: Locale
- Definition Classes
- UIElement
-
def
location: Point
- Definition Classes
- UIElement
-
def
locationOnScreen: Point
- Definition Classes
- UIElement
-
def
maximumSize: Dimension
- Definition Classes
- UIElement
-
def
maximumSize_=(x: Dimension): Unit
- Definition Classes
- UIElement
-
def
minimumSize: Dimension
- Definition Classes
- UIElement
-
def
minimumSize_=(x: Dimension): Unit
- Definition Classes
- UIElement
-
def
name: String
- Definition Classes
- Component
-
def
name_=(s: String): Unit
- Definition Classes
- Component
-
final
def
ne(arg0: AnyRef): Boolean
- Definition Classes
- AnyRef
-
final
def
notify(): Unit
- Definition Classes
- AnyRef
-
final
def
notifyAll(): Unit
- Definition Classes
- AnyRef
-
def
onFirstSubscribe(): Unit
- Attributes
- protected
- Definition Classes
- Component → UIElement → LazyPublisher
-
def
onLastUnsubscribe(): Unit
- Attributes
- protected
- Definition Classes
- UIElement → LazyPublisher
-
def
opaque: Boolean
- Definition Classes
- Component
-
def
opaque_=(b: Boolean): Unit
- Definition Classes
- Component
-
def
paint(g: Graphics2D): Unit
- Definition Classes
- Component
-
def
paintBorder(g: Graphics2D): Unit
- Attributes
- protected
- Definition Classes
- Component
-
def
paintChildren(g: Graphics2D): Unit
- Attributes
- protected
- Definition Classes
- Component
-
def
paintComponent(g: Graphics2D): Unit
- Attributes
- protected
- Definition Classes
- Component
-
lazy val
peer: JPanel
- Definition Classes
- Panel → Wrapper → Component → UIElement
-
def
preferredSize: Dimension
- Definition Classes
- UIElement
-
def
preferredSize_=(x: Dimension): Unit
- Definition Classes
- UIElement
-
def
publish(e: Event): Unit
- Definition Classes
- Publisher
-
val
reactions: Reactions
- Definition Classes
- Reactor
-
def
repaint(rect: Rectangle): Unit
- Definition Classes
- UIElement
-
def
repaint(): Unit
- Definition Classes
- UIElement
-
def
replace(existing: Component, replacement: Component): Unit
Replaces one component with another.
Replaces one component with another. Useful for dynamic layouts.
- existing
the component to be replaced
- replacement
the component replacing the existing one
-
def
requestFocus(): Unit
- Definition Classes
- Component
-
def
requestFocusInWindow(): Boolean
- Definition Classes
- Component
-
def
revalidate(): Unit
- Definition Classes
- Component
-
def
self: Component
- Definition Classes
- UIElement → Proxy
-
def
showing: Boolean
- Definition Classes
- UIElement
-
def
size: Dimension
- Definition Classes
- UIElement
-
def
subscribe(listener: Reaction): Unit
- Definition Classes
- LazyPublisher → Publisher
-
final
def
synchronized[T0](arg0: ⇒ T0): T0
- Definition Classes
- AnyRef
-
def
toString(): String
- Definition Classes
- Component → Proxy → AnyRef → Any
-
def
toolkit: Toolkit
- Definition Classes
- UIElement
-
def
tooltip: String
- Definition Classes
- Component
-
def
tooltip_=(t: String): Unit
- Definition Classes
- Component
-
def
unsubscribe(listener: Reaction): Unit
- Definition Classes
- LazyPublisher → Publisher
-
def
validate(): Unit
- Definition Classes
- UIElement
- def vertical: Group
- def vertical_=(value: Group): Unit
-
def
visible: Boolean
- Definition Classes
- UIElement
-
def
visible_=(b: Boolean): Unit
- Definition Classes
- UIElement
-
final
def
wait(): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long, arg1: Int): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
final
def
wait(arg0: Long): Unit
- Definition Classes
- AnyRef
- Annotations
- @throws( ... )
-
implicit
def
wrapPar(c: Component): GroupPanel.Element.Par
This method is needed for implicit resolution in Scala 2.10 due to some bug (?).
This method is needed for implicit resolution in Scala 2.10 due to some bug (?). It is not needed any more for Scala 2.11
- Attributes
- protected
-
implicit
def
wrapSeq(c: Component): GroupPanel.Element.Seq
This method is needed for implicit resolution in Scala 2.10 due to some bug (?).
This method is needed for implicit resolution in Scala 2.10 due to some bug (?). It is not needed any more for Scala 2.11
- Attributes
- protected
-
def
xLayoutAlignment: Double
- Definition Classes
- Component
-
def
xLayoutAlignment_=(x: Double): Unit
- Definition Classes
- Component
-
def
yLayoutAlignment: Double
- Definition Classes
- Component
-
def
yLayoutAlignment_=(y: Double): Unit
- Definition Classes
- Component
- object Par
- object Seq