Skip to content

Image

Header: winui/ui/media/Image.hpp. Spec: winui/ui/media/Image.hpp.

Shows an ImageSource (a still picture, an animated GIF or an SVG document) stretched into the space layout gives it.

The declarative spec is ui::Image, and the pixels themselves are a ui::Bitmap. Sizing follows the Stretch mode, Uniform by default. A nine-grid can keep the corners unscaled. An animated source plays by itself when its auto_play is set, and play() and stop() control it. It pauses while it is off screen (scrolled out of view, clipped away or not drawn) and goes on from the frame it showed when it comes back. The image takes no input.

Example:

// Declarative:
window.set_content(ui::Image{
    .source  = ui::Bitmap::read_png("photo.png"),
    .stretch = ui::Stretch::UniformToFill,
    .width   = 320.0f,
    .height  = 200.0f,
});

// Imperative, an animated GIF:
auto  stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* gif   = stack->add(std::make_unique<ui::ImageControl>(ui::AnimatedImage::read("spinner.gif")));
gif->set_stretch(ui::Stretch::None);
auto* pause = stack->add(std::make_unique<ui::ButtonControl>("Pause"));
pause->on_click([gif] { gif->is_playing() ? gif->stop() : gif->play(); });

ui::Image

An image: a still picture, an animated GIF or an SVG document, stretched into its space. Builds a ui::ImageControl. The pixels themselves are a ui::Bitmap.

source takes any of the three, since ui::ImageSource converts from each. It can be bound. A Binding doesn't compare its values, only the Property behind it does, so bind(choice, [](int i) -> ui::ImageSource { ... }) swaps pictures as choice changes.

Example:

std::vector<ui::Bitmap> photos = {ui::Bitmap::read("cliff.jpg"), ui::Bitmap::read("lake.jpg")};
ui::Property<int> choice{0};

window.set_content(ui::VerticalStack{.spacing = 8, .content = {
    ui::Image{.source  = ui::bind(choice, [&photos](int i) -> ui::ImageSource { return photos[i]; }),
              .stretch = ui::Stretch::UniformToFill,
              .width   = 300.0f,
              .height  = 200.0f},
    ui::Button{.text = "Next", .on_click = [&] { choice = (choice.get() + 1) % 2; }},
}});

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

ui::Image{
    .source, .stretch, .nine_grid, .style, .margin, .width, .height, .min_width, .min_height,
    .max_width, .max_height, .horizontal_alignment, .vertical_alignment, .visible, .tool_tip,
    .accessibility, .on_image_opened, .on_image_failed, .on_pointer_entered,
    .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down, .on_wheel, .control
}

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

Field Type Default
source ui::Binding<ui::ImageSource> The picture: a bitmap, an animated image or an SVG document. Empty draws nothing.
stretch ui::Binding<ui::Stretch> {ui::Stretch::Uniform} How the picture fills the element's box. Uniform, the default, keeps the aspect ratio.
nine_grid ui::Binding<ui::Thickness> The nine-grid insets, in the picture's pixels. The corners keep their size and the edges and center stretch. Zero, the default, draws the picture whole.
on_image_opened ui::Callback<void()> A source made by ImageSource::load() finished decoding (ImageControl::on_image_opened()).
on_image_failed ui::Callback<void(std::string_view)> A source made by ImageSource::load() failed, with the reason (ImageControl::on_image_failed()).
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::Image> Out: the handle that receives the built control.

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::ImageControl; the caller owns it.

ui::ImageControl

Derives from ui::Element.

Shows an ImageSource (a still picture, an animated GIF or an SVG document) stretched into the space layout gives it.

The declarative spec is ui::Image, and the pixels themselves are a ui::Bitmap. Sizing follows the Stretch mode, Uniform by default. A nine-grid can keep the corners unscaled. An animated source plays by itself when its auto_play is set, and play() and stop() control it. It pauses while it is off screen (scrolled out of view, clipped away or not drawn) and goes on from the frame it showed when it comes back. The image takes no input.

Example:

// Declarative:
window.set_content(ui::Image{
    .source  = ui::Bitmap::read_png("photo.png"),
    .stretch = ui::Stretch::UniformToFill,
    .width   = 320.0f,
    .height  = 200.0f,
});

// Imperative, an animated GIF:
auto  stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* gif   = stack->add(std::make_unique<ui::ImageControl>(ui::AnimatedImage::read("spinner.gif")));
gif->set_stretch(ui::Stretch::None);
auto* pause = stack->add(std::make_unique<ui::ButtonControl>("Pause"));
pause->on_click([gif] { gif->is_playing() ? gif->stop() : gif->play(); });

ImageControl

ImageControl() = default

An empty image. It measures to zero and draws nothing.

ImageControl

ImageControl(ImageSource source)

An image showing source. An animated source with auto_play set starts playing.

source

const ImageSource& source() const

The picture shown.

set_source

void set_source(ImageSource source)

Replaces the picture and updates layout. Starts an animated source playing when its auto_play says so. A source still loading through ImageSource::load() draws nothing until its decode is done. Then the control updates its layout and raises on_image_opened() or on_image_failed(). The window stays idle while the picture decodes.

on_image_opened

void on_image_opened(Callback<void()> handler)

Fires when a source made by ImageSource::load() has finished decoding and shows.

on_image_failed

void on_image_failed(Callback<void(std::string_view)> handler)

Fires when a source made by ImageSource::load() could not be read or decoded. The handler gets the reason.

stretch

Stretch stretch() const

How the picture fills the element's box. Uniform by default.

set_stretch

void set_stretch(Stretch stretch)

Sets the stretch mode. Updates layout and redraws.

nine_grid

const Thickness& nine_grid() const

The nine-grid insets, in the picture's pixels. Zero, the default, draws the picture whole. Ignored for an SVG source.

set_nine_grid

void set_nine_grid(const Thickness& nine_grid)

Sets the nine-grid insets. Redraws.

is_animated

bool is_animated() const

Whether the source has more than one frame.

is_playing

bool is_playing() const

Whether the animation plays. play() sets it and stop() clears it. An animation that ran to its end still reports true.

play

void play()

Restarts a stopped animation. Does nothing to one already running or one that ran to its end.

stop

void stop()

Stops and rewinds to the first frame.

current_frame

std::size_t current_frame() const

The index of the frame showing, 0 for a still source.

parallel_measure_safe

bool parallel_measure_safe() const override

Returns true, since measuring reads only the source's size.

on_screen_changed

void on_screen_changed(bool on_screen) override

Called after a frame in which on_screen() changed, for an element that called set_screen_tracking(). The frame that brings an element back into view calls it with true, so a control that paused its clock off screen restarts it here (request_tick_in() works from it). The default does nothing.

automation_role

AutomationRole automation_role() const override

What screen readers and other assistive tools see of this node: the role, name, help text and children here, and what a client can do with the node through the providers automation_providers() lists (ui/Automation.hpp).

automation_role() is the role this node reports; Group by default.

placement

Placement placement() const

Computes the Placement for the current bounds and source.

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.

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().

tick_override

void tick_override(Duration delta) override

This element's own part of tick(): a clock of its own (ToolTip's hover delay, an animation's phase, a caret blink). Runs after the visual layer has advanced and before the children are ticked, which tick() does by itself. Tick only parts that for_each_child() does not list here. Default: nothing.

struct Placement

Where the picture lands and which part of it shows, for the current bounds.

Field Type Default
content Rect The whole picture's rect, in DIPs. It may extend past the element.
visible Rect The part of the element's drawing that isn't clipped away.

Tests

  • tests/test_image.cpp