GroupPanel
A panel that uses javax.swing.GroupLayout to visually arrange its components.
Note: This is a slightly adapted variant of the original
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 (
structural types.
GroupPanel
classby 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-basedstructural 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
comprehensive explanation.
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 acomprehensive 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.
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.
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
that everything from the package
placed inside a scala.swing.SimpleSwingApplication like this:
functionality of
GroupPanel
s. For all examples, it is assumedthat everything from the package
scala.swing
is imported and the code isplaced inside a scala.swing.SimpleSwingApplication like this:
{{{
import scala.swing._
import de.sciss.swingplus._
import scala.swing._
import de.sciss.swingplus._
object Example extends SimpleSwingApplication {
lazy val top = new MainFrame {
contents = new GroupPanel {
// example code here
}
}
}
}}}
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.
in a horizontal sequence but share the same vertical space.
{{{
val label = new Label("Label:")
val textField = new TextField(20)
val label = new Label("Label:")
val textField = new TextField(20)
horizontal = Seq(label, textField)
vertical = Par(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.
strange. To get better behaviour, the components' vertical sizes can be
linked together.
{{{
linkVerticalSize(label, textField)
}}}
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:
the vertical, parallel group. To achieve this, the vertical layout line
should be written this way:
{{{
vertical = Par(Leading, FixedSize)(label, textField)
}}}
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:
about that further down), the following code also prevents (vertical)
resizing:
{{{
vertical = Par(Baseline)(label, textField)
}}}
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.
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))
}}}
horizontal = Seq(label, textField)
vertical = Par(label, Size(textField, 50, 100, 200))
}}}
The
that can be used for any of the minimum, preferred and maximum arguments.
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
provided convenience methods
Size.apply
, one can also use theprovided convenience methods
Size.fixed
and Size.fill
.Because the default alignment in a parallel group is
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:
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))
}}}
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
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 ofCONSTANT_ASCENT
orCONSTANT_DESCENT
. -
Elements with a baseline resize behavior of
OTHER
orCENTER_OFFSET
are not resizable.
Since a text field's resizing behaviour is
not resizable when used with baseline alignment.
CENTER_OFFSET
, it isnot resizable when used with baseline alignment.
==Gaps==
The
components and along the container edges. To see the difference, try turning
this feature off manually by inserting the following lines:
GroupPanel
turns on automatic creation of gaps betweencomponents and along the container edges. To see the difference, try turning
this feature off manually by inserting the following lines:
{{{
autoGaps = false
autoContainerGaps = false
}}}
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.
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()
)
horizontal = Seq(
Gap.Container(),
label,
Gap.Preferred(Related),
textField,
Gap.Container()
)
vertical = Seq(
Gap.Container(),
Parallel(label, textField),
Gap.Container()
)
}}}
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
preferred and maximum size) between components are created with
the
Gap
object:{{{
bc.. horizontal = Seq(
label,
Gap(10, 20, 100),
textField
)
bc.. horizontal = Seq(
label,
Gap(10, 20, 100),
textField
)
vertical = Seq(
Par(label, Gap(30), textField)
)
}}}
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.
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
a bit of syntax sugar available with the
and
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 isa 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()
)
}}}
bc.. horizontal = Seq(
Gap.Container(),
label,
Gap.Spring(), // default is Related
textField,
Gap.ContainerSpring()
)
}}}
These create gaps that minimally are as wide as a
be - it is possible to specify whether the
should be used - but can be resized to an arbitrary size.
Gap.Preferred
wouldbe - it is possible to specify whether the
Related
or Unrelated
distanceshould be used - but can be resized to an arbitrary size.
{{{
bc.. horizontal = Seq(
Gap.Container(),
label,
Gap.Spring(Unrelated),
textField,
Gap.ContainerSpring()
)
}}}
bc.. horizontal = Seq(
Gap.Container(),
label,
Gap.Spring(Unrelated),
textField,
Gap.ContainerSpring()
)
}}}
The preferred size can also be specified more closely (
or
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)
)
}}}
bc.. horizontal = Seq(
Gap.Container(),
label,
Gap.Spring(Unrelated, Size.Infinite),
textField,
Gap.ContainerSpring(Size.Infinite)
)
}}}
Please note that
'''only''' be used inside a sequential group.
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
version of the "Find" dialog presented as example
for
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")
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)
)
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
)
)
}}}
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
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:
The
Seq.apply
and Par.apply
methods take a sequence of GroupPanel.Element
instances, and thereforeyou 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)
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)
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 ) }
)
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)
}: _*
)
}}}
params.map { p =>
Par(Center)(p.check, p.label, p.slider, p.index)
}: _*
)
}}}
As can be seen, the
calls to the
Seq
and Par
classes can be instantiated and then populated throughcalls to the
contents
member.- Authors
- Hanns Holger RutzAndreas 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
Value members
Methods
Indicates whether the visibility of components is considered for the layout.
If set to
Defaults to
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
space. Defaults to
layout. If set to
false
, invisible components still take upspace. Defaults to
true
.Links the sizes (horizontal and vertical) of several components.
- Value Params
- comps
-
the components to link
Fields
Inherited methods
def scala$swing$LazyPublisher$$super$unsubscribe(listener: Reaction): Unit
- Inhertied from
- LazyPublisher
def deafTo(ps: Publisher*): Unit
Installed reaction won't receive events from the given publisher anylonger.
- Inhertied from
- Reactor
def listenTo(ps: Publisher*): Unit
Listen to the given publisher as long as deafTo isn't called for
them.
them.
- Inhertied from
- Reactor
override def unsubscribe(listener: Reaction): Unit
- Definition Classes
- LazyPublisher -> Publisher
- Inhertied from
- LazyPublisher
override def subscribe(listener: Reaction): Unit
- Definition Classes
- LazyPublisher -> Publisher
- Inhertied from
- LazyPublisher