UI Components: Masked Pane
This example shows how to set up a Masked Pane
using Indigo's general UI system.
What is a 'Masked Pane'
Pane, as in a pane of glass. A rectangular transparent sheet you can see through, but not a 'window' all by itself.
Masking (also sometimes known as clipping) is used to show content within a defined area, and hide any content outside of that area. An example in familiar GUI terms: If you had a window (with scrolling disabled) and you resized it to be smaller than the content, you'd expect the content outside it not to show.
A masked pane is in fact the same as a scroll pane, without the scrolling.
Neither masked panes nor scroll panes have the sort of layout control you get with component groups and lists, but you can always place one of those inside the pane.
Example Links
Setting up a MaskedPane
Much like the other examples, we need to define our components, and they've been placed in a separate object.
As well as the masked pane, we also need something to put in it. In this case, we're going to make a masked pane that is half the size of a label, so that you can see the masking in action.
object CustomComponents:
val labelBounds = Bounds(0, 0, 150, 20)
val label: Label[Int] =
Label[Int](
"Count: 0",
(_, label) => labelBounds
) { case (offset, label, dimensions) =>
Outcome(
Layer(
TextBox(label)
.withColor(RGBA.White)
.moveTo(offset.unsafeToPoint)
.withSize(dimensions.unsafeToSize)
.withFontSize(20.pixels)
)
)
}
.withText((i: Int) => "Count: " + i)
val pane: MaskedPane[Label[Int], Int] =
MaskedPane(
BindingKey("masked pane"),
labelBounds.dimensions / 2,
label
)
When using masked and scroll panes, you need to remember to register the shaders they use. The
all
import is conveniently a Set
, so you can just concatenate it onto any other shaders
you are using.
val shaders: Set[ShaderProgram] =
Set() ++ indigoextras.ui.shaders.all
For the present function, we're going to render something slightly more elaborate than usual.
Rendering a masked pane is the same as rendering any other component, but so that you can see where the label and the mask are, we're also going to render a couple of shapes illustrating their boundaries.
def present(context: Context[Unit], model: Model): Outcome[SceneUpdateFragment] =
val ctx = UIContext(context.forSubSystems, Size(1), 1)
.moveBoundsBy(Coords(50, 50))
.copy(reference = model.count)
val labelBounds =
CustomComponents.labelBounds.unsafeToRectangle.moveBy(50, 50)
val labelBorder =
Shape.Box(
labelBounds,
Fill.None,
Stroke(1, RGBA.Green)
)
val maskBorder =
Shape.Box(
labelBounds.resize(labelBounds.size / 2),
Fill.None,
Stroke(1, RGBA.Cyan)
)
model.component
.present(ctx)
.map(c => SceneUpdateFragment(c).addLayer(labelBorder, maskBorder))