Render Commands¶
Header: winui/render/Renderer.hpp.
struct FillRoundedRect¶
Recorded form of Renderer::fill_rounded_rect(): a solid rounded-rect fill.
Example:
// What Renderer::fill_rounded_rect() draws. In an element of your own:
class Panel : public ui::Element
{
void render_override(ui::Renderer& r, const ui::RenderContext& context) const override
{
const ui::Theme& theme = context.theme;
r.fill_rounded_rect(bounds(), 4.0f, theme.colors().background);
}
};
| Field | Type | Default | |
|---|---|---|---|
bounds |
Rect |
The rect to fill, in DIPs. | |
radius |
float |
0.0f |
Corner radius in DIPs; 0 is a plain rect. |
color |
Color |
The fill color. |
struct FillRoundedRectGradient¶
A gradient fill of a rounded rect (radius 0 == a plain rect), along the brush's GradientAxis: the XP/Vista counterpart to FillRoundedRect's flat color. Recorded form of Renderer::fill_rounded_rect_gradient().
| Field | Type | Default | |
|---|---|---|---|
bounds |
Rect |
The rect to fill, in DIPs. | |
radius |
float |
0.0f |
Corner radius in DIPs. |
brush |
LinearGradientBrush |
Stops and axis, mapped across bounds. |
struct FillEllipse¶
A filled ellipse, given by its center and radii. A circle (radius_x == radius_y) given this way has none of the rounding ambiguity of a square rect with a corner radius. The classic circular radio-button bevel is built from these.
Example:
renderer.fill_ellipse(ui::Point{10, 10}, 6, 6, ui::colors::White);
renderer.stroke_ellipse(ui::Point{10, 10}, 6, 6, ui::colors::Black, 1.0f);
| Field | Type | Default | |
|---|---|---|---|
center |
Point |
Center, in DIPs. | |
radius_x |
float |
0.0f |
Horizontal radius, in DIPs. |
radius_y |
float |
0.0f |
Vertical radius, in DIPs. |
color |
Color |
The fill color. |
struct FillPolygon¶
A straight-edged polygon filled with one color, such as a menu's cascade arrow. The shape is always closed; StrokePolyline outlines one.
Example:
renderer.fill_polygon(ui::FillPolygon{
.points = {{0, 10}, {8, 0}, {16, 10}},
.fill = ui::colors::White,
});
| Field | Type | Default | |
|---|---|---|---|
points |
std::vector<Point> |
Vertices in DIPs, in drawing order. | |
fill |
Color |
Interior color; transparent paints nothing. |
struct FillRectPattern¶
Recorded form of Renderer::fill_rect_pattern(): bounds filled with a Pattern in two colors.
Example:
// The Windows 95 scrollbar trough.
const ui::Rect trough{0, 16, 16, 200};
renderer.fill_rect_pattern(trough, ui::Pattern::Checkerboard50, ui::colors::White,
ui::Color::from_rgba(192, 192, 192));
| Field | Type | Default | |
|---|---|---|---|
bounds |
Rect |
The rect to fill, in DIPs. | |
pattern |
Pattern |
Pattern::Checkerboard50 |
Which stipple. |
fg |
Color |
The "on" pixels. | |
bg |
Color |
The "off" pixels. |
struct StrokeRoundedRect¶
Recorded form of Renderer::stroke_rounded_rect(): a solid outline of a rounded rect, centered on bounds' edge (so half of stroke_width falls outside it).
| Field | Type | Default | |
|---|---|---|---|
bounds |
Rect |
The rect the stroke is centered on, in DIPs. | |
radius |
float |
0.0f |
Corner radius in DIPs; 0 is a plain rect. |
color |
Color |
The stroke color. | |
stroke_width |
float |
1.0f |
Pen width in DIPs. |
struct StrokeRoundedRectGradient¶
The stroke counterpart of FillRoundedRectGradient, used for Fluent's elevation border: a linear gradient stroked around a rounded rect. The gradient spans extent_px device pixels whatever the height of bounds (Fluent uses 3). A positive span starts at bounds.top() and brush's stops read top to bottom. A negative span starts at bounds.bottom() and the stops read bottom to top, which is how an accent control's flipped border is drawn.
| Field | Type | Default | |
|---|---|---|---|
bounds |
Rect |
The rect the stroke is centered on, in DIPs. | |
radius |
float |
0.0f |
Corner radius in DIPs. |
brush |
LinearGradientBrush |
Stops only; the axis is always vertical. | |
extent_px |
float |
3.0f |
Signed gradient span in device pixels (see above). |
stroke_width |
float |
1.0f |
Pen width in DIPs. |
struct StrokeEllipse¶
Recorded form of Renderer::stroke_ellipse(): an ellipse outline centered on its edge.
| Field | Type | Default | |
|---|---|---|---|
center |
Point |
Center, in DIPs. | |
radius_x |
float |
0.0f |
Horizontal radius, in DIPs. |
radius_y |
float |
0.0f |
Vertical radius, in DIPs. |
color |
Color |
The stroke color. | |
stroke_width |
float |
1.0f |
Pen width in DIPs. |
struct StrokeLine¶
A single straight stroked line segment. The classic raised and sunken bevels (a light edge and a dark edge around a flat fill) are built from these: draw_bevel_border() draws four per bevel ring.
| Field | Type | Default | |
|---|---|---|---|
from |
Point |
Start point, in DIPs. | |
to |
Point |
End point, in DIPs. | |
color |
Color |
The stroke color. | |
stroke_width |
float |
1.0f |
Pen width in DIPs. |
struct StrokeWavyLine¶
A wavy line from from to to, the underline that marks a SpellChecker's misspellings. It is a command of its own, so TestRenderer records one squiggle per word instead of many line segments, and each backend draws its own curve. The wave swings amplitude DIPs to either side of the from→to axis, one full period every wavelength DIPs, starting on the axis.
| Field | Type | Default | |
|---|---|---|---|
from |
Point |
Start of the axis, in DIPs. | |
to |
Point |
End of the axis, in DIPs. | |
color |
Color |
The stroke color. | |
stroke_width |
float |
1.0f |
Pen width in DIPs. |
amplitude |
float |
1.0f |
Peak distance from the axis, in DIPs. |
wavelength |
float |
4.0f |
Length of one full period along the axis, in DIPs. |
struct StrokePolyline¶
Straight lines through points in order, with the closing back()-to-front() edge only when closed. TeachingTip's tail outlines its two slanted sides this way and leaves open the side it shares with the tip's body.
Example:
const std::vector<ui::Point> tail = {{0, 10}, {8, 0}, {16, 10}};
renderer.fill_polygon(ui::FillPolygon{.points = tail, .fill = ui::colors::White});
renderer.stroke_polyline(ui::StrokePolyline{
.points = tail,
.color = ui::Color::from_rgba(0, 0, 0, 40),
.stroke_width = 1.0f,
.closed = false, // the two slanted sides only
});
| Field | Type | Default | |
|---|---|---|---|
points |
std::vector<Point> |
Vertices in DIPs, in drawing order. | |
color |
Color |
Line color; transparent draws nothing. | |
stroke_width |
float |
1.0f |
Line width in DIPs; 0 draws nothing. |
closed |
bool |
false |
Draw the closing back()-to-front() edge too. |
struct DrawTextRun¶
Recorded form of Renderer::draw_text(): a UTF-8 string laid out in bounds per style.
Example:
// What Renderer::draw_text() draws. In an element of your own:
class Label : public ui::Element
{
void render_override(ui::Renderer& r, const ui::RenderContext& context) const override
{
const ui::Theme& theme = context.theme;
r.draw_text("Save", bounds(), theme.colors().text, theme.type.body);
}
};
| Field | Type | Default | |
|---|---|---|---|
text |
std::string |
The UTF-8 text. | |
bounds |
Rect |
Layout box the text is aligned within, in DIPs. | |
color |
Color |
Text color. | |
style |
TextStyle |
Font, size, weight, alignment and wrapping. |
struct DrawGeometry¶
A PathGeometry, filled and then stroked: what every Shape draws. Either brush may be absent. A gradient's relatively mapped points span brush_box and its absolute ones count from its top-left; a Shape passes its own layout rect, since a Shape's brushes map to the element's top-left. An empty brush_box means the geometry's own bounds.
An AcrylicBrush fill depends on drawing order: it blurs whatever was drawn under the geometry before this command, so it must come after the content it sits on. Window::render() draws popups last, so a popup's acrylic blurs the finished tree.
Example:
renderer.draw_geometry(ui::DrawGeometry{
.geometry = ui::PathGeometry::ellipse(ui::Point{50, 50}, 40, 40),
.fill = ui::Brush{ui::colors::White},
.stroke = ui::Brush{ui::Color::from_rgba(0, 120, 215)},
.stroke_style = {.thickness = 2.0f},
});
| Field | Type | Default | |
|---|---|---|---|
geometry |
PathGeometry |
The figures to paint, in DIPs. | |
fill |
std::optional<Brush> |
Interior paint; nullopt skips the fill. | |
stroke |
std::optional<Brush> |
Outline paint; nullopt skips the stroke. | |
stroke_style |
StrokeStyle |
Pen for stroke; ignored when there is none. |
|
brush_box |
Rect |
Box gradients map to; empty uses the geometry's bounds. |
struct DrawImage¶
A Bitmap stretched into bounds per stretch, as drawn by Image, Icon and PersonPicture. Renderer::draw_image() takes the Bitmap itself, while DrawImageNineSlice looks an image up by name. The recorded command keeps image_size but not the pixels. A backend reads the pixels during the call.
Example:
const ui::Bitmap photo = ui::Bitmap::read("avatar.jpg", {.max_width = 96, .max_height = 96});
renderer.draw_image(photo, ui::Rect{0, 0, 48, 48}, ui::Stretch::UniformToFill);
| Field | Type | Default | |
|---|---|---|---|
image_size |
Size |
Source pixel dimensions, for Stretch's aspect-ratio math. | |
bounds |
Rect |
Destination rect, in DIPs. | |
stretch |
Stretch |
Stretch::Uniform |
How the image is fitted into bounds. |
struct DrawImageRect¶
A source rectangle of a caller-supplied bitmap (in its pixels) drawn into bounds, scaled on each axis to fill it. The Image element's stretch modes and nine-grid are built from it: cropping (Stretch None or UniformToFill on a smaller element) is a smaller source rect, and a nine-grid is nine of these. Like DrawImage, the recorded command keeps the image's size but not its pixels.
Example:
// Draw the top-left 16x16 tile of a sprite sheet at 2x.
const ui::Bitmap sheet = ui::Bitmap::read_png("sprites.png");
renderer.draw_image_rect(sheet, ui::Rect{0, 0, 16, 16}, ui::Rect{0, 0, 32, 32});
| Field | Type | Default | |
|---|---|---|---|
image_size |
Size |
Source pixel dimensions. | |
source |
Rect |
The part of the image drawn, in image pixels. | |
bounds |
Rect |
Destination rect, in DIPs. |
struct DrawImageNineSlice¶
A nine-slice image blit: looks image_name up in whatever ImageThemePack the backend was configured with and draws it stretched (corners verbatim, edges stretched along one axis, center stretched along both) to fill bounds, per slice_margins. libwinui ships no images itself. Without a matching image, or without a pack, which is the common case, the call draws nothing and is no error, so a control can always ask for image chrome.
Example:
| Field | Type | Default | |
|---|---|---|---|
image_name |
std::string |
Key into the backend's ImageThemePack. | |
bounds |
Rect |
Destination rect, in DIPs. | |
slice_margins |
Thickness |
Corner/edge sizes, in the source image's pixels. |
struct DrawSvg¶
An SvgImage drawn into bounds: its viewport is its natural size (or bounds' size when it has none), scaled on each axis to bounds. The recorded command keeps the natural size, not the markup.
Example:
const ui::SvgImage logo = ui::SvgImage::read("logo.svg"); // throws if unreadable
renderer.draw_svg(logo, ui::Rect{0, 0, 64, 64});
| Field | Type | Default | |
|---|---|---|---|
natural_size |
Size |
The document's own size in DIPs; empty when it declares none. | |
bounds |
Rect |
Destination rect, in DIPs. |
struct PushClip¶
Restricts subsequent drawing to bounds until the matching PopClip. Pushes and pops must balance within a single render() call. A container that clips, such as Scroll, pushes before rendering its children and pops right after.
Example:
void render_visible(ui::Renderer& renderer, const ui::RenderContext& context, const ui::Rect& viewport,
const std::vector<ui::Element*>& visible_children)
{
renderer.push_clip(viewport);
for (const ui::Element* child : visible_children)
{
child->render(renderer, context);
}
renderer.pop_clip();
}
| Field | Type | Default | |
|---|---|---|---|
bounds |
Rect |
The clip rect, in DIPs (transformed by any enclosing PushTransform). |
struct PushClipEllipse¶
An elliptical clip, such as PersonPicture's circular photo mask. It ends with the same PopClip as a rect clip. A backend that pops each kind of clip differently tracks which kind it pushed.
Example:
const ui::Bitmap photo = ui::Bitmap::read("avatar.jpg");
renderer.push_clip_ellipse(ui::Point{24, 24}, 24, 24);
renderer.draw_image(photo, ui::Rect{0, 0, 48, 48}, ui::Stretch::UniformToFill);
renderer.pop_clip();
| Field | Type | Default | |
|---|---|---|---|
center |
Point |
Center of the ellipse, in DIPs. | |
radius_x |
float |
0.0f |
Horizontal radius, in DIPs. |
radius_y |
float |
0.0f |
Vertical radius, in DIPs. |
struct PopClip¶
Ends the innermost PushClip, PushClipEllipse or PushClipRoundedRect.
struct PushTransform¶
An affine transform of everything drawn until the matching PopTransform: a point p lands at (p.x * scale + p.y * m21 + offset.x, p.x * m12 + p.y * scale_y + offset.y) (a Matrix in render/PathGeometry.hpp's row-vector convention), and nested pushes compose (the inner one applies first). Scroll zooms this way: its content is laid out once at its natural size and drawn magnified. scale_y differs from scale for ScaleToFit's Stretch::Fill. m12 and m21 are non-zero only for a rotation, which comes from Element::set_rotation(). Clips pushed inside a transform are transformed with it, and a rotated clip becomes its bounding box. Pushes and pops balance within one render() call.
Example:
void render_zoomed(ui::Renderer& renderer, const ui::RenderContext& context, const ui::Element& content)
{
renderer.push_transform(2.0f, ui::Point{10, 10}); // uniform 2x zoom, then offset
content.render(renderer, context);
renderer.pop_transform();
}
scale¶
Horizontal scale (Matrix::m11); also the vertical one when uniform.
offset¶
Translation applied after scaling, in DIPs.
scale_y¶
Vertical scale (Matrix::m22).
m12¶
Rotation/skew term; 0 unless the element is rotated.
m21¶
Rotation/skew term; 0 unless the element is rotated.
matrix¶
The same transform as a Matrix.
struct PopTransform¶
Ends the innermost PushTransform.
struct PushOpacityLayer¶
Draws every call between this and the matching PopOpacityLayer as one layer at opacity. Flyouts and dialogs fade in and out this way: Window::render() wraps a flyout's popup, or a dialog's scrim and content together, in one layer at Element::transition_opacity().
At opacity 1, which is every Element that isn't fading, the layer must look like no layer, and a backend may skip it. At 0 or below a backend need not draw the contents at all. Pushes and pops must balance within a single render() call, like PushClip and PopClip, and the two kinds nest inside each other freely.
Example:
void render_faded(ui::Renderer& renderer, const ui::RenderContext& context, const ui::Element& popup_content)
{
renderer.push_opacity_layer(0.5f);
popup_content.render(renderer, context); // drawn at half opacity, as one layer
renderer.pop_opacity_layer();
}
| Field | Type | Default | |
|---|---|---|---|
opacity |
float |
1.0f |
Layer opacity, 0..1; 1 is a no-op. |
struct PopOpacityLayer¶
Ends the innermost PushOpacityLayer.
struct SetRasterization¶
The rasterization policy for the whole frame. Window::render() emits it once, at the start, from Theme::rasterization. Because it is a command, TestRenderer records it and describe() prints it, so a test can check that a theme asks for aliased rendering without comparing pixels. The backend applies snap_to_device_pixels while drawing, by rounding each primitive's coordinates to the nearest device pixel, so it stays correct under DPI scaling. Layout is unaffected and stays resolution-independent.
Example:
// A renderer of your own receives it first in every frame and keeps it for what follows.
class ExportRenderer : public ui::Renderer
{
public:
void set_rasterization(const ui::SetRasterization& policy) override
{
antialias_shapes_ = policy.geometry == ui::GeometryAA::Antialiased;
text_smoothing_ = policy.text;
snap_to_pixels_ = policy.snap_to_device_pixels;
}
private:
bool antialias_shapes_ = true;
ui::TextAA text_smoothing_ = ui::TextAA::Grayscale;
bool snap_to_pixels_ = false;
};
| Field | Type | Default | |
|---|---|---|---|
geometry |
GeometryAA |
GeometryAA::Antialiased |
Edge policy for shapes and lines. |
text |
TextAA |
TextAA::Grayscale |
Text smoothing policy. |
snap_to_device_pixels |
bool |
false |
Round primitives to device pixels. |
Tests¶
tests/test_render.cpp