Class BackdropPanel


public final class BackdropPanel extends Padding
A panel whose background is the content behind it, put through a BackdropEffect: a glass control bar over video, a washed header over a picture, a redacted field. Wraps one child with insets, like the Padding it extends, and paints the effect under it.

frosted(Widget) is the one most applications want: frosted glass in the theme's own tone, light or dark as the palette is, with an optional tint.

Put it where it will be painted after what it sits over. The effect samples what the frame has already drawn, so a panel is a sibling after the content: in a stack, the layer above; in a column, nothing sits behind it and the effect shows the window's own background, which is a legitimate but very dull result.

Costs a batch break and a copy of its own bounds per frame it paints. That is a control bar's worth of work, not a list row's: a panel per row is the wrong shape for this widget, and a hundred of them will show up in the frame time.

Degrades to a flat panel in the effect's tint where the renderer has no backdrop support, with the same size, position and corner radius.

A redaction painted here is a picture, and not a fact. The effect covers whatever the frame has already drawn — siblings this panel holds no reference to and knows nothing about — so a screen reader still reads the covered widget's text, verbatim, from the covered widget's own node. Blurring a card number hides it from the person looking at the screen and from nobody else. An application that means to redact something hides or ignores the widget that holds it, and uses the panel for what the eye is shown.

The panel itself is never in the accessible tree: it declares no role, no name, no state and no action, so it is scaffolding, and a reader hears the controls inside it with the glass nowhere in the telling. It says its painting is decoration, so it is removed in silence.

Size axis: the corner radius follows the resolved ControlSize row like every other component's chrome, unless setCornerRadius(float) pins it. The effect's own dimensions (a rim width, a cell size) are authored in points and do not scale with the step: they are optical properties of the material, not of the control.

  • Field Details

  • Constructor Details

    • BackdropPanel

      public BackdropPanel(BackdropEffect effect, Widget<?> child)
      A panel with 12pt of padding around child.
    • BackdropPanel

      public BackdropPanel(BackdropEffect effect, Insets insets, Widget<?> child)
      A panel with insets of padding around child.
      Parameters:
      effect - what the panel does to the pixels behind it; never null
      insets - the padding
      child - the content
  • Method Details

    • paintsFromBackdrop

      protected boolean paintsFromBackdrop()
      Yes: this panel's picture is the pixels behind it, put through an effect.

      Which is why it is stale whenever they change, without anything about the panel moving. Answering this is what puts its rectangle into the damage of a frame that repaints anything underneath it, and without it partial rendering could not be trusted over one of these.

      Overrides:
      paintsFromBackdrop in class Widget<Padding>
      Returns:
      whether the pixels behind this widget are part of what it draws
    • backdropReach

      protected float backdropReach()
      The widest reach of its effects: each one reads what is around the shape as the frame left it, since the effects before it write only inside the shape, so the stack reads no further than its farthest member.
      Overrides:
      backdropReach in class Widget<Padding>
      Returns:
      the reach, 0 or more
    • frosted

      public static BackdropPanel frosted(Widget<?> child)
      A panel of frosted glass with 12pt of padding around child; see frosted(Insets, Widget).
      Parameters:
      child - the content
      Returns:
      the panel
    • frosted

      public static BackdropPanel frosted(Insets insets, Widget<?> child)
      A panel of frosted glass that follows the theme: what is behind it is blurred, then calmed and laid over with the palette's surface tone, so that text on it reads. The tone is read from the theme in effect at each paint: light over a light palette, dark over a dark one, and the other one the moment the palette changes, with nothing for the application to do. A bar that stays while a page scrolls under it, or a panel of controls over a picture.

      setTint(limn.graphics.Color, float) mixes a colour of the application's into that tone. A panel that needs something else, another blur, a refracting rim, takes its effects by hand with setEffects(limn.graphics.BackdropEffect...), and is then no longer worked out from the theme.

      Parameters:
      insets - the padding
      child - the content
      Returns:
      the panel
    • isFrosted

      public final boolean isFrosted()
      Returns:
      whether this panel's effects are worked out from the theme, as frosted makes it
    • setTint

      public BackdropPanel setTint(@Nullable Color colour, float amount)
      Mixes a colour into frosted glass: the glass keeps the theme's light or dark tone and leans toward colour by amount. A brand colour at a tenth is a hint of it; at a half the glass is plainly that colour.
      Parameters:
      colour - the colour to lean toward, or null for none; its alpha is not used
      amount - how far, from 0 (the theme's tone alone) to 1 (the colour alone)
      Returns:
      this panel
      Throws:
      IllegalStateException - when this panel is not frosted
      IllegalArgumentException - if amount is not a number; one past either end is clamped to it
    • tint

      public final @Nullable Color tint()
      Returns:
      the colour mixed into frosted glass, or null
    • tintAmount

      public final float tintAmount()
      Returns:
      how far frosted glass leans toward tint(), 0 to 1
    • setCover

      public BackdropPanel setCover(float cover)
      Sets how much of its tone frosted glass lays over what is behind it: the less, the more of what is behind shows through, blurred, and the less the text on the glass is helped against it. 0.6 unless set, which reads over any content; a panel over a picture chosen for it can take less.
      Parameters:
      cover - from 0, where what is behind is blurred and nothing more is laid on it, to 1, where the tone hides it
      Returns:
      this panel
      Throws:
      IllegalStateException - when this panel is not frosted
      IllegalArgumentException - if cover is not a number; one past either end is clamped to it
    • cover

      public final float cover()
      Returns:
      how much of its tone frosted glass lays over what is behind, 0 to 1
    • effect

      public final BackdropEffect effect()
      The first effect painted behind the child; see effects() for the whole stack.
    • effects

      public final List<BackdropEffect> effects()
      Every effect painted behind the child, in the order they are drawn.
    • setEffects

      public BackdropPanel setEffects(BackdropEffect... stack)
      Replaces the effect with a stack, drawn in order over the same shape.

      This is how the effects compose, and it needs nothing from the renderer: each one reads the framebuffer and writes it, so the second samples what the first left. It is what makes a frosted pane out of the pieces rather than out of a variant with four parameters, and what makes BackdropEffect.Blur affordable, since a separable blur IS two passes.

      Each pass costs one batch flush and one copy of the panel's own bounds, so a stack of three costs three of each. Over a small panel that is cheap and over a full-window one it is not; the cost is proportional to the shape, not to the window.

      Throws:
      IllegalArgumentException - if the stack is empty
    • setEffect

      public BackdropPanel setEffect(BackdropEffect newEffect)
      Replaces the effect. UI thread only.
    • setCornerRadius

      public BackdropPanel setCornerRadius(float radius)
      Pins the corner radius in points, or RADIUS_FROM_TOKENS to follow the resolved size row (the default). UI thread only.
    • paintsDecoration

      protected boolean paintsDecoration()
      The glass is material and carries no information, so the tree removes this panel without saying anything about it.

      Every other member of the scaffolding family — the rows, the columns, the paddings — paints nothing at all and is removed in silence for free. This one paints, and the guard that catches a custom gauge nobody named cannot tell a gauge from a wash: it would name a toolkit class in an application's log, for a picture that is background over content this panel does not own, and recommend marking it ignored — which would take the whole child subtree with it and delete the very controls the glass sits behind.

      Overrides:
      paintsDecoration in class Widget<Padding>
      Returns:
      true, always
    • onPaint

      protected void onPaint(Canvas canvas)
      Description copied from class: Widget
      Widget's own background/content, in local coordinates.
      Overrides:
      onPaint in class Widget<Padding>