ImageWidget
ImageWidget — displays a raster image (PNG, WebP) with a configurable sizing policy, content-fit mode, and intra-box alignment.
Public functions
ImageWidget
| Returns | Function |
|---|---|
| Constructors | |
Self | new(icon: &RasterIcon) |
Self | from_raw(pixels: Vec<u8>, width: u32, height: u32) |
| Builder methods | |
Self | mask(shape: ImageMaskShape) |
Self | fit(fit: ImageFit) |
Self | alignment(alignment: Alignment) |
Self | width(w: f32) |
Self | height(h: f32) |
Self | size(w: f32, h: f32) |
Self | resizable(resizable: bool) |
Self | alt(text: impl Into<String>) |
Self | a11y_hidden() |
Detailed description
Unlike IconWidget which is designed
for small square tintable icons, ImageWidget handles arbitrary aspect
ratios and defaults to full-color rendering.
Sizing model
Two independent concerns, mirroring Qt's QLabel/QPixmap, SwiftUI's
Image, and CSS's replaced-element model:
- Box size — how big the widget's layout rectangle is.
width/height/sizepin a fixed logical extent. A pinned axis is rigid: it is reported as-is and is never scaled up to a parent's proposal (this is the SwiftUI.frame(width:height:)/ Qt fixed-size behaviour). Pinning only one axis derives the other from the image's aspect ratio (CSSwidth: Npx; height: auto).- With no axis pinned the widget reports its natural pixel size.
By default (
resizable= true) a constraining proposal scales that natural size down/up while preserving aspect ratio;resizable(false)locks it to the raw pixel dimensions (SwiftUI's default non-.resizable()image).
- Content fit — how the image pixels map into that box, via
ImageFit(Contain/Cover/Fill/ScaleDown/None, the CSSobject-fitset) plusalignment(the CSSobject-positionequivalent) for where slack/overflow lands. Modes that overflow the box (Cover, andNoneon an oversized image) are clipped to the box so the image never bleeds past its layout rectangle.
For a fixed 32×32 logo: ImageWidget::new(icon).size(32.0, 32.0) — the
box is exactly 32×32 and the artwork is letterboxed inside it
(Contain, the default).
#![allow(unused)] fn main() { use teksilo_canvas::RasterIcon; use teksilo_widgets::primitives::image_widget::{ImageWidget, ImageFit}; use teksilo_widgets::primitives::image_mask::ImageMaskShape; // A 64×64 source pinned to a fixed 32×32 box, with no masking. let icon = RasterIcon::from_raw(vec![255; 64 * 64 * 4], 64, 64); let _logo = ImageWidget::new(&icon).size(32.0, 32.0); // Cover a square avatar slot and crop to a circle. let _avatar = ImageWidget::new(&icon) .mask(ImageMaskShape::Circle) .fit(ImageFit::Cover) .alt("User avatar") .size(48.0, 48.0); }
API reference
📖 Full rustdoc API for this module
pub struct ImageWidget
Import: use teksilo::widgets::ImageWidget;
A widget that displays a raster image (PNG, WebP, or raw RGBA pixels) with configurable fit and alignment.
#![allow(unused)] fn main() { pub struct ImageWidget { /* fields */ } }
Methods
pub fn new(icon: &RasterIcon) -> Self
Create from a decoded RasterIcon (e.g., from res!()).
The texture is named after the icon's identity, so every
ImageWidget showing this icon (or a clone of it) in a window shares
one texture, uploaded once.
That texture, with its mip chain, stays on the GPU for the life of the
window and is never freed, even once no widget shows the icon. Each
distinct identity keeps one: a clone of the icon is the same identity,
but the same bytes decoded again are a new one. Keep decoded images
in an application cache and clone them, rather than decoding per
widget or per realization: in a virtualized grid of runtime images
such as album covers, a cover decoded each time its tile scrolls into
view leaves one more texture behind each time. Pixels shown once, or
that change, belong on from_raw, whose texture
goes with the widget; a mask takes that path too.
pub fn from_raw(pixels: Vec<u8>, width: u32, height: u32) -> Self
Create from raw RGBA pixel data, width × height, tightly packed.
The pixels are written once into a live source when the widget is
first built, without a copy. The window's texture for them goes with
the first frame that does not draw the widget (it was destroyed,
scrolled out or parked), so GPU memory follows what is on screen,
and comes back with one upload. Input the source refuses (a zero
side, a buffer shorter than width × height × 4, a side over
LiveImageSource::MAX_DIMENSION) draws nothing, and says so once
on standard error.
pub fn mask(mut self, shape: ImageMaskShape) -> Self
Apply an anti-aliased alpha mask to the image at construction time. The pixels are first centre-cropped to the shorter side (so the mask shape is geometrically consistent regardless of the source aspect ratio), then their alpha channel is modulated by the mask coverage. RGB is preserved.
Cover fit is the natural pairing — the masked square fills
the avatar/thumbnail bounds and the masked-out corners stay
transparent. Contain works but may letterbox. The default
fit (Contain) is left unchanged so callers explicitly pick
a fit when they apply a mask.
The masked picture is the widget's own, written once into a live
source as from_raw's is: the icon's shared
texture is left alone. ImageMaskShape::None is a no-op.
pub fn fit(mut self, fit: ImageFit) -> Self (default: ImageFit::Contain)
Set the content-fit mode — how the image pixels map into the box.
See ImageFit.
pub fn alignment(mut self, alignment: Alignment) -> Self
Set where the fitted image sits within the box when the active fit
leaves slack or crops (the CSS object-position analogue). Defaults
to Alignment::CENTER. Leading/Trailing resolve against the
active layout direction (RTL-aware).
pub fn width(mut self, w: f32) -> Self (default: None)
Pin a fixed display width (in logical pixels). The width axis
becomes rigid — reported as-is and never scaled to a parent
proposal. With no height pinned, the height derives from the
image's aspect ratio (CSS width: Npx; height: auto).
pub fn height(mut self, h: f32) -> Self (default: None)
Pin a fixed display height (in logical pixels). The height axis becomes rigid. With no width pinned, the width derives from the image's aspect ratio.
pub fn size(mut self, w: f32, h: f32) -> Self
Pin both display width and height (in logical pixels). The box is
exactly this size, rigid on both axes; the image content is fitted
inside it via the fit mode. This is the
fixed-size-logo case — .size(32.0, 32.0).
pub fn resizable(mut self, resizable: bool) -> Self
Control whether, with no axis pinned, a constraining parent
proposal scales the natural pixel size (true, the default) or the
box stays locked to the raw pixel dimensions (false). Equivalent
to opting out of SwiftUI's .resizable(). No effect once a
dimension is pinned via width /
height / size.
pub fn alt(mut self, text: impl Into<String>) -> Self (default: None)
Set the accessibility alt text.
pub fn a11y_hidden(mut self) -> Self
Mark this image as decorative — hidden from the accessibility
tree. Use when the image's semantic content is already conveyed
by adjacent text (e.g. a hero image next to its caption). ARIA
equivalent of alt="" / role="presentation".