Skip to content

ScaleToFit

Header: winui/ui/containers/ScaleToFit.hpp. Spec: winui/ui/containers/ScaleToFit.hpp.

One child, laid out at its natural size and drawn scaled to the room the ScaleToFit is given.

The child is measured with unbounded room, then drawn with a scale that stretch() and stretch_direction() decide. The ScaleToFit asks for the scaled size. Text is scaled like a picture and keeps its line breaks and font size. Pointer input is mapped back through the same scale, so the child's controls keep working at any size. A popup opened from inside is anchored where its owner would be unscaled. With UniformToFill, the overflow is clipped.

Example:

auto  box   = std::make_unique<ui::ScaleToFitControl>();
auto* label = box->set_child(std::make_unique<ui::TextControl>("42"));
box->set_stretch(ui::Stretch::Uniform);
box->set_stretch_direction(ui::StretchDirection::UpOnly); // grow the text, never shrink it
window.set_content(std::move(box));
label->set_text("43"); // the observer stays valid, and the next layout scales the new text

// Or declaratively, filling a 200x100 slot:
window.set_content(ui::ScaleToFit{
    .width   = 200.0f,
    .height  = 100.0f,
    .content = ui::Text{.text = "42"},
});

ui::ScaleToFit

One child scaled to the room it is given. Builds a ui::ScaleToFitControl.

The child is measured at its natural size and then scaled, text and strokes included.

Example:

window.set_content(ui::ScaleToFit{
    .stretch           = ui::Stretch::Uniform,
    .stretch_direction = ui::StretchDirection::DownOnly, // shrink to fit, never enlarge
    .width             = 200.0f,
    .height            = 100.0f,
    .content           = ui::Text{.text = "Scaled to fit", .role = ui::TextRole::Header},
});

Every field, in the order a designated initializer names them:

ui::ScaleToFit{
    .stretch, .stretch_direction, .style, .margin, .width, .height, .min_width, .min_height,
    .max_width, .max_height, .horizontal_alignment, .vertical_alignment, .visible, .tool_tip,
    .accessibility, .on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus,
    .on_key_down, .on_wheel, .control, .content
}

Fields, in declaration order (designators must follow it):

Field Type Default
stretch ui::Binding<ui::Stretch> {ui::Stretch::Uniform} How the child's natural size maps to the box. Uniform, the default, keeps its aspect ratio.
stretch_direction ui::Binding<ui::StretchDirection> {ui::StretchDirection::Both} UpOnly only enlarges, DownOnly only shrinks, and Both, the default, does both.
on_pointer_entered ui::Action The pointer moved onto the control.
on_pointer_exited ui::Action The pointer left the control.
on_got_focus ui::Action Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this.
on_lost_focus ui::Action Keyboard focus left the control and everything inside it.
on_key_down ui::Callback<ui::Handling(const ui::KeyEvent&)> A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()).
on_wheel ui::Callback<ui::Handling(const ui::PointerEvent&)> A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()).
control ui::ControlOut<ui::ScaleToFit> Out: the handle that receives the built control.
content ui::Spec The scaled child.

Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, accessibility every spec carries (conventions).

build

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::ScaleToFitControl; the caller owns it.

ui::ScaleToFitControl

Derives from ui::Element.

One child, laid out at its natural size and drawn scaled to the room the ScaleToFit is given.

The child is measured with unbounded room, then drawn with a scale that stretch() and stretch_direction() decide. The ScaleToFit asks for the scaled size. Text is scaled like a picture and keeps its line breaks and font size. Pointer input is mapped back through the same scale, so the child's controls keep working at any size. A popup opened from inside is anchored where its owner would be unscaled. With UniformToFill, the overflow is clipped.

Example:

auto  box   = std::make_unique<ui::ScaleToFitControl>();
auto* label = box->set_child(std::make_unique<ui::TextControl>("42"));
box->set_stretch(ui::Stretch::Uniform);
box->set_stretch_direction(ui::StretchDirection::UpOnly); // grow the text, never shrink it
window.set_content(std::move(box));
label->set_text("43"); // the observer stays valid, and the next layout scales the new text

// Or declaratively, filling a 200x100 slot:
window.set_content(ui::ScaleToFit{
    .width   = 200.0f,
    .height  = 100.0f,
    .content = ui::Text{.text = "42"},
});

ScaleToFitControl

ScaleToFitControl() = default

An empty ScaleToFit, Uniform and Both.

set_child

template <typename T>
T* set_child(std::unique_ptr<T> child)

Takes ownership of the single child and returns a non-owning observer. Replaces and destroys any previous child, and nullptr clears it. Updates layout.

child

Element* child() const

The child, or nullptr. Owned by the ScaleToFit.

stretch

Stretch stretch() const

How the child is scaled. Uniform by default. None never scales, and Fill scales each axis to its room. Uniform fits inside the room and UniformToFill covers it, both keeping the aspect ratio.

set_stretch

void set_stretch(Stretch stretch)

Changes the stretch. Updates layout.

stretch_direction

StretchDirection stretch_direction() const

Whether scaling may enlarge, shrink or both. Both by default.

set_stretch_direction

void set_stretch_direction(StretchDirection direction)

Changes which way scaling may go. Updates layout.

scale_x

float scale_x() const

The horizontal scale the child is drawn at, from the last arrange. 1 before the first.

scale_y

float scale_y() const

The vertical scale, like scale_x(). The two differ only under Stretch::Fill.

compute_scale_factor

Size compute_scale_factor(Size available, Size content) const

The x and y factors that fit content into available under the current stretch() and stretch_direction(). An unbounded axis (ui::Unbounded) uses the other axis's factor. With both unbounded, the factors are 1 and 1.

for_each_child

void for_each_child(ChildVisitor visit) const override

The child, when there is one.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Implement these instead of measure() and arrange(). The base class has already applied margin, size limits and alignment by the time these run. measure_override() is pure (every concrete Element must size itself); arrange_override() defaults to a no-op since most leaves need nothing beyond having bounds() set, which the base already did.

arrange_override

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Position children within the rect arrange() settled on (also in bounds(), window DIPs). Default: nothing, for a leaf.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

What the control draws, in layout coordinates; render() has already applied the visual layer. A container draws its children through their render(), never their render_override().

map_to_children

Point map_to_children(Point p) const override

The point as the scaled child sees it.

children_hit_clip

std::optional<Rect> children_hit_clip() const override

The child is clipped to the bounds, and can be hit only inside them.

Enumerations

StretchDirection

enum class StretchDirection

Which way a ScaleToFit may scale its child.

Value
UpOnly Only enlarge. A child bigger than the room is drawn at its natural size.
DownOnly Only shrink. A child smaller than the room is drawn at its natural size.
Both The default. Scale either way.

Tests

  • tests/test_layout_panels.cpp