Images

In Reflex, images aren't widgets. They're layers painted into the bg: (or fg:) of any styled object, inheriting the same states, transitions, layout, and hover hooks as everything else.

Conventions used in every diagram:

  • Dashed grey rectangle is the object's bounds (whatever the style is applied to).
  • Solid blue rectangle is the image being rendered into those bounds.
  • Labels match the parameter names you'd see in the stylesheet snippet beside the diagram.

Where image files live and how to reference them

Before any Image(source: ...) resolves, the file must live somewhere the build can bundle, and a @Bitmap declaration must point at it. In a Reflex C++ app, every bundled asset goes under the app's resources/ folder:

MyApp/
├── code/
├── resources/
│   ├── styles.glx
│   ├── icons/
│   └── images/
└── resources.xml

The two ways to write a path:

Inside @Bitmap, path: accepts two forms.

A bare relative path resolves against the current stylesheet's bundled resources folder. Common when the image lives in your own app:

@Bitmap piece_wp: { path: "pieces/Chess_plt45.png"; antialias: true; };

A fully-qualified :res: path spells out the namespace prefix. Use it for cross-namespace or SDK-provided resources:

@Bitmap logo:  { path: ":res:myapp/images/logo.png"; };
@Font   Title: { path: ":res:expenses/fonts/Satoshi-Bold.otf"; size: 18; };

Both forms work for @Bitmap and @Font.

The @Bitmap declaration parameters

Putting it all together:

@Bitmap logo:
{
    path: "images/logo.png";       /* required: relative or :res: path */
    density: 2;                    /* optional: Retina density factor */
    antialias: true;               /* optional: smooth scaling */
    tile: 24;                      /* optional: sprite-sheet tile size */
};

Reference it later by the declared name:

Header: { bg: Image(source: logo; fit: contain); };

The fit: modes

The fit: parameter controls how the image sizes inside its object's bounds. Three values: cover, contain, stretch.

fit: cover

Image fills the bounds, cropping if needed. Same idea as CSS background-size: cover. The image scales up so both axes fully cover the object's bounds. Whatever overflows the longer axis is clipped.

object bounds image (overflows, cropped) fit: cover
LeftPanel:
{
    size: 360, auto;
    bg:
        Fill(colour: black),
        Image(source: left_bg; fit: cover);   /* background image fills the panel */
};

When to use: hero panels, full-bleed backgrounds, anywhere you want the image to fill the area without distortion and don't mind cropping the edges.

fit: contain

Image fits entirely within the bounds, no cropping. Same idea as CSS background-size: contain. The shorter axis leaves spare room. Use anchor: (covered below) to position the image within that room.

object bounds image (whole, left) spare fit: contain
PlayPauseButton:
{
    size: 28, 28;
    bg: Image(source: play_icon; fit: contain);
};

When to use: icons, logos, anything where you want the whole image visible and you control the container size.

fit: stretch

Image distorts to fill the bounds exactly. The image is non-uniformly scaled so both axes match the bounds exactly. Aspect ratio is not preserved.

object bounds image (stretched to fit) fit: stretch
DistortedBackground:
{
    bg: Image(source: noise_tile; fit: stretch);
};

When to use: rarely. Mostly for textures or noise patterns where aspect doesn't matter. For everything else prefer cover or contain.


The anchor: parameter

With fit: contain, the image leaves spare room on the longer axis. anchor: chooses where in that room the image sits. Values match the nine-point alignment grid:

top_left, top, top_right, left, center, right, bottom_left, bottom, bottom_right.

anchor: left img anchor: center img anchor: right img

Combine with indent: to add a uniform inset.

TrackItem:
{
    size: auto, 32;
    bg:
        Text(font: Medium; value: &value; justify: left; indent: 12, 4),
        Image(source: trash_icon; fit: contain; anchor: right; indent: 10);
};

The trash icon sits flush-right inside the 32px-tall row, with a 10px inset. The text uses its own indent for the left padding. Both layers share the same bounds and claim different parts of it.


Sprite frames with >

When a @Bitmap is declared with tile: it's a sprite sheet. Pick a named frame with the > operator inside Image(source: ...).

@Bitmap icons:
{
    path: ":res:icons.png";
    tile: 24;                /* each frame is 24x24 */
};

ToolbarPlay:  { bg: Image(source: icons > play;  fit: contain); };
ToolbarStop:  { bg: Image(source: icons > stop;  fit: contain); };
ToolbarMute:  { bg: Image(source: icons > mute;  fit: contain); };

For continuous value-driven animation across all frames (rotary knobs, dials, scrubbing), use ImageArray instead.


Combining Image with other layers

Layers in bg: stack bottom-to-top. Mix Image with Fill, Border, Text, Line, Gradient to compose richer visuals.

PromoQueenW:
{
    inherit: PromoButton;
    bg:
        Fill(colour: sq_light; corner: 6),               /* tile background */
        Image(source: piece_wq; fit: contain);           /* piece on top */

    @State hover:
    {
        bg:
            Fill(colour: white; corner: 6),
            Image(source: piece_wq; fit: contain);
    };
};

The Fill paints a rounded tile, the Image paints the chess piece on top of it. Hovering swaps the fill colour but keeps the piece.


Image at a corner or edge using AddFloat

To pin a fixed-size image at one of the nine alignment points of a parent (close-X button, corner badge, watermark, floating action button), reach for the layout side, not just the style side. The recipe:

  1. Make a child object (the carrier) and give it a style with size: plus bg: Image(...).
  2. AddFloat that child onto the parent at the alignment point you want.
m_panel (parent) inline content underneath img AddFloat TopRight
// In view.cpp
GLX::AddFloat(m_panel, m_close_btn, GLX::kAlignmentTopRight);
/* In styles.glx */
CloseButton:
{
    size: 24, 24;
    margin: 8;
    bg: Image(source: close_icon; fit: contain);
};

The image renders into m_close_btn's 24x24 box. The box sits at the panel's top-right corner with an 8px margin. Swap kAlignmentTopRight for any of the nine alignment values to pin the icon to a different corner or edge.

The same pattern covers:

  • Floating action buttons: kAlignmentBottomRight
  • Watermarks: kAlignmentBottomLeft
  • Centered modals carrying a hero image: kAlignmentCenter
  • Badge dots over an avatar: kAlignmentTopRight
  • Pinned status icons ("LIVE", "NEW"): kAlignmentTopLeft

The carrier object is just a normal styled GLX::Object. It can receive hover states, click events, transitions, everything else.