Module charts
Expand description
§Charts and plots
Tilo charts are retained widgets, not a separate canvas runtime. They use the same layout, theme, pointer, accessibility and automation paths as every other UI control. Import the focused facade:
use ui::charts::{CartesianPlot, PlotDatum, PlotSeries, SeriesKind};§Choosing a chart
| Question | Widget |
|---|---|
| Trend or relationship on numeric axes | CartesianPlot |
| Tiny inline trend | Sparkline |
| Categories side-by-side, stacked or cumulative | CategoryChart |
| Distribution from raw observations | Histogram or DistributionChart |
| Part-to-whole | PieChart |
| Seasonal or cyclical magnitude | PolarAreaChart |
| Hierarchical part-to-whole | SunburstChart or Treemap |
| Matrix intensity | Heatmap |
| Several metrics with different scales | RadarChart |
| Open/high/low/close data | CandlestickChart |
| Movement between stages | SankeyChart |
| Dense weighted relationships | ChordChart |
| Schedule, interval, reservation, or milestone | RangeChart |
| Sequential conversion | FunnelChart |
| Compact progress, a countdown or a threshold value | ProgressRing / BarGauge |
CartesianPlot series may be mixed. It supports line, smooth line, step,
straight/smooth/stepped area, scatter, bubble, bar and lollipop marks. CategoryChart supports grouped,
stacked, normalized-percent and waterfall layouts. DistributionChart supports
box, violin and combined plots. FunnelChart also renders pyramids.
RangeChart covers timelines, Gantt plans, numeric intervals and milestones,
with an optional per-range progress fill and caller-defined value formatter.
PolarAreaChart (rose/radial bars) keeps equal angular categories while radius
encodes magnitude; its radius_exponent supports perceptual area scaling.
ChordChart automatically allocates weighted node arcs and connection ribbons;
both are independently hoverable, clickable, and accessible.
§Interactive gallery
The catalogue is executable example code rather than a static mockup. It contains populated examples of every chart family, reflows from two columns to one, and retains each widget’s hover, click, focus, and accessibility behavior:
cargo run -p ui-examples --example ui-lab -- chart_galleryDeterministic wide/narrow, light/dark renders use the same scene:
cargo run -p ui-examples --example ui-shots -- chart_gallery_shots§A mixed Cartesian plot
use ui::charts::*;
let observations = vec![
PlotDatum::new("jan", 1.0, 18.0)
.labelled("January")
.with_detail("18 ms median"),
PlotDatum::new("feb", 2.0, 12.0)
.labelled("February")
.with_detail("12 ms median"),
];
let latency = PlotSeries::new("latency", "Latency", accent, observations)
.kind(SeriesKind::Smooth)
.stroke_width(2.5)
.marker_radius(4.0)
.marker_shape(MarkerShape::Diamond)
.dashed([8.0, 4.0]);
let style = CartesianStyle {
x_axis: PlotAxis::default().with_ticks([
AxisTick::new(1.0, "Jan"),
AxisTick::new(2.0, "Feb"),
]),
y_axis: PlotAxis::default()
.fixed(0.0, 25.0)
.formatted(AxisLabelFormat::Decimal(1))
.with_tick_density(7, 64.0)
.titled("Milliseconds"),
rendering: CartesianRenderingStyle {
show_crosshair: true,
show_markers_on_lines: true,
..Default::default()
},
zoom: CartesianZoom::X,
pan: CartesianPan::X,
brush: CartesianBrush::X,
..Default::default()
};
let plot = CartesianPlot::new(vec![latency])
.with_annotations([
CartesianAnnotation::y_line("budget", 20.0, warning)
.labelled("Latency budget"),
CartesianAnnotation::x_band("deploy", 1.25, 1.5, accent)
.labelled("Deployment"),
])
.with_style(style)
.titled("Request latency")
.with_height(320.0);Every datum has a stable id. Hover selects the nearest datum, the inspector
uses its label/detail, clicking queues (series_id, datum_id), and assistive
technology receives the same actionable data points. Read activations with
take_activated() during the parent widget’s update pass.
Touch starts inspection immediately but only activates on a stationary
release; scrolling, dragging, cancellation, and a second finger suppress the
tap. Every interactive chart family—including legends, dense matrices, radial
hierarchies, flows, and timelines—uses the same tap-versus-scroll semantics, so
narrow layouts remain operable without stealing page navigation.
Series support straight, smooth, and stepped lines as well as straight,
smooth, and stepped areas. Use dashed() for arbitrary stroke patterns,
fill_alpha() for area density, MarkerShape::{Circle,Square,Diamond} for
point identity, and bar_radius() for square through pill-like bars. These
remain ordinary PlotSeries builders, so changing visual grammar does not
change the data or interaction model.
Represent absent observations with PlotDatum::missing(id, x). Lines and
areas break at missing or scale-invalid samples by default, preventing charts
from implying continuity that the data does not contain. Opt into interpolation
with .connect_gaps(true). Per-series .show_markers(bool) overrides the
plot-wide marker preference when visual density differs between series.
Connected series automatically use contiguous min/max sampling when their
point count exceeds the responsive pixel budget. This preserves thin spikes
and troughs while preventing resize, zoom, and animation frames from
tessellating an invisible surplus of points. The original data remains intact
for exact hover, keyboard inspection, activation, domains, and accessibility.
Tune or disable the behavior with
CartesianStyle::with_adaptive_sampling(enabled, points_per_pixel).
Auto/fixed domains are cached and invalidated on data, style, or visibility
changes instead of being rescanned on every frame. For finite ascending-X
series, add .sorted_by_x() to use an exact binary-window hover search; the
full scan remains the safe default for unsorted data and during animation.
Use CartesianAnnotation for labelled X/Y reference lines, highlighted X/Y
intervals, and point callouts. Their coordinates stay in data space through
resize, logarithmic projection, zoom, and pan. Constructors and fluent
labelled, stroke_width, solid, and fill_alpha modifiers cover common
analytical overlays; set_annotations() and annotations_mut() support live
updates and fully dynamic dashboards. Annotations are hoverable, clickable,
keyboard/assistive-technology activatable, and may provide richer tooltip text
with with_detail(). Read their stable id with take_annotation_activated(),
focus one from application state with inspect_annotation(), or opt decorative
ink out of interaction with .interactive(false).
Cartesian inspectors can follow the pointer or remain anchored to the datum
with tooltip_anchor. tooltip_width, tooltip_min_width, tooltip_offset,
and tooltip_padding control geometry, while tooltip_show_coordinates
appends values formatted by each axis. Auto placement flips and clamps against
all chart edges, including very narrow cards.
set_series() smoothly interpolates matching series and datum ids by default,
so live updates preserve visual identity and hit testing follows the painted
positions. Tune animation_duration or disable animate_changes; the active
theme’s reduced-motion preference always snaps immediately, and animation
frame requests stop when the transition completes.
Identical updates are no-ops rather than animation restarts. Use
set_series_immediate(), finish_animation(), and is_animating() for initial
hydration, deterministic capture, or application-controlled motion.
Generated ticks automatically become sparser as a plot narrows. Use
with_tick_density(maximum, minimum_spacing) to tune that responsive policy,
and AxisLabelFormat::{Auto, Decimal, Compact, Percent, Bytes, Scientific, Duration, Currency} for common formatting without allocating explicit ticks.
Durations adapt seconds into microseconds through days. Explicit ticks remain
the fully custom/date-label escape hatch and preserve their first and last
values while intermediate labels are sampled to fit.
Call PlotAxis::logarithmic() for a base-10 axis. Non-positive values are
excluded consistently from rendering, hit testing, keyboard navigation, and
accessibility. Auto domains use multiplicative padding, generated ticks prefer
decades, and zoom, pan, viewport restoration, and brush selections remain in
the caller’s original data coordinates.
Axis titles configured with PlotAxis::titled() reserve their own responsive
layout space and render on both axes; long titles are Unicode-safely elided
instead of colliding with ticks or escaping narrow chart bounds.
Set zoom to CartesianZoom::X for timelines or CartesianZoom::XY for
exploratory plots. Wheel zoom is anchored under the pointer and requires
Ctrl/Command by default, so charts embedded in scrollable pages do not trap
navigation. Native trackpad pinch zoom is also cursor-anchored and needs no
modifier because the gesture is unambiguous. Dedicated chart surfaces can set
zoom_modifier_required: false.
Use viewport(), take_view_changed(), and reset_view() to synchronize
plots, persist navigation, or provide a visible reset control. Restore or link
views with set_viewport(), move them using pan_view_by() or the
size-independent pan_view_fraction(), and turn a brush directly into a detail
view with zoom_to_selection().
Set pan to CartesianPan::X or CartesianPan::XY for direct navigation.
Alt-drag pans by default, and native trackpad pan gestures use the same
scale-aware behavior when Alt is held. Dedicated chart surfaces may set
pan_modifier_required: false. Logarithmic axes pan multiplicatively while
viewport() and change events continue to expose ordinary data coordinates.
Set brush to CartesianBrush::X for interval selection or
CartesianBrush::XY for rectangular selection. Shift-drag starts a brush by
default, preserving ordinary scrolling and inspection; set
brush_modifier_required: false on a dedicated analysis surface. Read the
domain-space result with take_brush_selection()—the result is independent of
the chart’s pixel size and ready for filtering, aggregation, or linked views.
Visible legends are interactive by default. Clicking or assistive activation
toggles a series, automatically recomputes auto-ranges, and removes hidden data
from hit testing. Applications can use set_series_visible(),
is_series_visible(), and take_visibility_changed() to restore or synchronize
that state. Set interactive_legend: false for a purely descriptive legend.
§Part-to-whole
PieChart::set_slices() interpolates values by stable slice id, including
growing newly introduced slices from zero. Painted arcs and hit testing share
the same animated sectors. Configure animate_changes and
animation_duration in PieStyle; reduced-motion themes always snap and stop
requesting animation frames.
PieStyle also exposes tooltip enablement, pointer-versus-slice anchoring,
fixed or content width, minimum width, offset, padding, optional percentages,
and a right inset for adjacent legends. Placement flips and clamps at all
edges while protecting donut center labels. This lets compact dashboards,
touch-first drill-downs, and externally labelled pies share the same widget
without accepting one hard-coded overlay.
Identical slice updates do not restart motion; set_slices_immediate(),
finish_animation(), and is_animating() provide explicit lifecycle control.
PieChart::with_legend() names every slice (swatch, label and share) beside
the chart when there is room and under it otherwise; the wedges shrink to make
room, so the legend never overlaps them. Prefer it to a legend painted beside
the chart.
let chart = PieChart::new(vec![
PieSlice::new("src", "Source", 48.0, blue).with_detail("48 GB"),
PieSlice::new("target", "Build output", 21.0, amber).with_detail("21 GB"),
// Visible and inspectable, but does not pretend there is an action.
PieSlice::new("reserved", "Reserved", 4.0, gray).non_interactive(),
])
.with_style(PieStyle {
inner_radius: 0.64, // zero makes a conventional pie
gap: 0.018,
hover_offset: 7.0,
..Default::default()
})
.with_center("69 GB", "total size");Use SunburstChart when the data has more than one hierarchy level. Its node
values may be explicit or <= 0, in which case they are derived recursively
from children. Use Treemap when label density matters more than radial form.
§Raw distributions
Callers do not need to calculate bins, quartiles, outliers or density curves:
let histogram = Histogram::new(samples.clone(), accent)
.bins(24)
.normalize(HistogramNormalization::Density)
.titled("Response distribution")
.build();
let comparison = DistributionChart::new(vec![
DistributionSeries::new("before", "Before", blue, before),
DistributionSeries::new("after", "After", green, after),
])
.kind(DistributionKind::BoxAndViolin);§Updating live data
Use set_series, set_data, set_cells, set_items, set_stages, or the
type-specific setter. These preserve the retained widget and avoid rebuilding
the surrounding tree. Stable item IDs preserve semantic identity. Charts do no
work while idle and request no animation frames by themselves.
Large connected series are adaptively reduced to spike-preserving min/max
buckets based on the current plot width. Full-resolution data remains in the
widget for domains, exact inspection, activation, accessibility, and export;
use .sorted_by_x() for binary-window hover lookup. Tune or disable rendering
reduction through CartesianStyle::with_adaptive_sampling(...). Applications
may still pre-aggregate when domain-specific semantics require it.
A dashboard that samples on a timer can keep each history in a
RollingSeries: a fixed-capacity window whose as_slice() reads oldest to
newest and feeds Series, Sparkline or a Chart directly.
§Painting inside a custom-drawn surface
An app that lays out its own canvas (a dashboard card grid, a scrolling strip) still uses the toolkit’s charts, painting them at a known rect instead of hand-rolling paths:
| Painter | Draws |
|---|---|
Chart::draw_styled | Rolling line/area series with grid, header/legend and axis labels |
Sparkline::paint | One trend of f32 or f64 samples; Sparkline::points returns where each sample lands |
ProgressRingFace::paint / BarGauge::draw_styled | A fraction as a ring or labelled bar |
BarGauge::draw_span | A from..=to span on a track, e.g. a day’s low–high on the week’s range |
FunctionPlane::paint_grid | The pan/zoom grid, axes and 1-2-5 tick labels of a function plot; labels are Caption, culled on collision and kept whole inside the canvas |
TrendStyle chooses line width, a flat or gradient fill, straight or
Catmull-Rom joins, per-sample markers and a leading dot on the newest sample;
TrendRange maps values onto the plot (up_to(max), or fit(&data) for the
data’s own span). ChartStyle and GaugeStyle carry the grid, header and
track choices. Colours still resolve from the Theme at paint time.
§Customization and theming
Use ChartPalette::from_theme(theme) for a visually balanced categorical
palette plus sequential and diverging ramps that follow the active light,
dark, or high-contrast design tokens. categorical(index) wraps stably for
any series count; sequential(0.0..=1.0) and diverging(-1.0..=1.0) clamp
safely. ChartPalette::new(primary, additional) and the with_* methods make
fully custom, non-empty palettes concise.
Each family has a public *Style structure whose fields can be set with struct
update syntax. Geometry, labels, legends, grids, inspectors, value annotations,
colors and chart-specific behavior are independent. Supplying None for an
optional color delegates it to the OS theme. Explicit data colors are useful
when color has domain meaning; never copy surface or text colors out of a
particular light/dark theme.
Every chart takes its style through .with_style(style). Most fixed-canvas
charts expose .with_height(...); RangeChart intentionally derives its height from row count and RangeStyle::row_height, keeping Gantt
rows readable as data changes.
Tooltips, focus outlines, dim text, grids and surfaces always resolve from the
current Theme during drawing. This keeps the same chart intentional in light,
dark and high-contrast modes.
§Interaction and accessibility
- Every chart family supports arrows, Home, End, Enter, Space, and Escape using
the same inspection and activation state as pointer input.
inspect(...)andclear_inspection()expose that state to apps. - Comparative Cartesian, category, and radar legends are controls by default. Hidden
series are dimmed and excluded from pointer, keyboard, and accessibility
inspection. Visibility can be restored, persisted, or linked with
set_series_visible(),toggle_series(),is_series_visible(), andtake_visibility_changed(); setinteractive_legend: falsefor a purely descriptive legend. Legends wrap at narrow widths and reserve matching plot space, including long-label truncation and full-row hit targets. - Hover and click geometry comes from the exact same projection as painting.
- Inspectors avoid window edges and contain the stable label plus formatted detail supplied by the caller.
- Every actionable slice, stage, node, link, candle, cell and data point is a synthetic accessibility button with stable identity.
- Accessibility activation queues the same result as pointer activation.
- Empty, invalid and non-finite values are ignored safely instead of poisoning domains or geometry.
When a click changes application state, expose the same capability as a
resource action. Chart activation is a presentation input, not a replacement
for the application’s semantic action surface.
This module is a convenience facade. Chart widgets remain available from
crate::widget for consistency with the rest of the toolkit.
Modules§
- legend
- Shared responsive legend layout for comparative charts.
Structs§
- Axis
Tick - BarGauge
- Labelled horizontal bar gauge — a rounded track with a left-anchored fill, a top-left label and a right-aligned value readout.
- Candle
- Candlestick
Chart - Candlestick
Style - Cartesian
Animation Style - Cartesian
Annotation - Presentation for an analytical annotation. Annotations deliberately use data coordinates, so they remain correct through resize, zoom, and pan.
- Cartesian
Brush Selection - Cartesian
Gesture Policy - Cartesian
Plot - Cartesian
Rendering Style - Cartesian
Style - Cartesian
Tooltip Style - Category
- Category
Chart - Category
Chart Style - Category
Series - Category
Value - Chart
- Full time-series line/area chart with grid, axis ticks and a legend.
- Chart
Item - One valued item: a funnel stage, a polar wedge, a treemap tile.
- Chart
Labels - The text a
Chartpaints around its plot. Any of them may be empty. - Chart
Link - A weighted link from one
ChartNodeto another, by node id. - Chart
Node - A node of a flow or relationship graph (sankey, chord).
- Chart
Palette - Chart
Style - Grid, header and series styling for a
Chart. - Chord
Chart - Chord
Style - Distribution
Chart - Distribution
Series - Distribution
Style - Function
Plane - A function plot’s viewport:
centeris the world point under the canvas centre andscalethe pixels per world unit (y up in world space). - Funnel
Chart - Funnel
Style - Gauge
Reading - What an immediate-mode gauge paints: how full it is, in which colour, and the text around it. Either string may be empty.
- Gauge
Style - Paint options of a
BarGauge. - Gauge
Thresholds - Fraction thresholds (inclusive lower bounds) at which a gauge switches
from success → warning → danger. The default mirrors the dashboard
convention:
>= 0.85danger,>= 0.6warning, else success. - Heatmap
- Heatmap
Cell - Heatmap
Style - Histogram
- Fluent builder that bins raw samples and returns a normal
CartesianPlot, retaining all of its axis, tooltip and activation APIs. - PieChart
- A responsive interactive pie/donut widget.
- PieSlice
- A stable, labelled radial datum. Negative and non-finite values are treated
as zero.
idsurvives sorting and updates and is returned on activation. - PieStyle
- Visual configuration shared by pies and donuts.
- Plot
Axis - Axis configuration. Explicit ticks are useful for dates and categories;
when empty, the plot produces
tick_countcompact numeric labels. - Plot
Datum - Plot
Series - Polar
Area Chart - Polar
Area Style - Radar
Axis - Radar
Chart - Radar
Legend Style - Radar
Series - Radar
Style - Range
Bar - One interval. Equal start/end values render as milestones.
- Range
Chart - Range
Style - Rolling
Series - A fixed-capacity rolling window of samples — the store behind a live,
streaming chart or sparkline. Once full, every
pushdrops the oldest sample, soas_slicealways reads oldest → newest and can be handed straight toSeriesorSparkline::paint. - Sankey
Chart - Sankey
Style - Series
- One labelled data series.
datais sampled left-to-right across the plot;coloris the line/area tint;legendis the label shown in the chart’s legend strip (empty string = omit from the legend). - Sparkline
- Compact, chromeless trend line — no axis, grid, title or legend. Just the area fill + line, sized to fill its rect. Drop one into a table cell or a stat card to show a recent trend.
- Sunburst
Chart - Sunburst
Node - Sunburst
Style - Treemap
- Treemap
Style - Trend
Range - The value interval a trend maps onto its plot’s height:
lolands on the baseline,hion the top edge, and values outside clamp to the plot. - Trend
Style - Paint options for a trend line — the one line/area renderer behind
Sparklineand every series of aChart.
Enums§
- Axis
Label Format - Built-in axis label formats for common presentation needs.
- Axis
Range - Axis
Scale - Cartesian
Annotation Kind - A reference line, highlighted interval, or labelled point rendered in the plot’s data coordinate system.
- Cartesian
Brush - Cartesian
Pan - Cartesian
Tooltip Anchor - Cartesian
Zoom - Category
Layout - Chord
Selection - Distribution
Kind - Funnel
Direction - Histogram
Normalization - Legend
Position - Marker
Shape - PieTooltip
Anchor - Sankey
Selection - Series
Kind - Trend
Curve - How consecutive samples are joined.
- Trend
Fill - How the area under a trend line is filled.
Functions§
- format_
tick - A tick value at the precision its
stepimplies, without trailing zeros:0.5,2,-0.25, and exactly0at the origin. - nice_
step - The 1-2-5 “nice” step nearest above
rawworld units. Non-finite or non-positive input falls back to1.0. - nice_
ticks - Axis ticks on a 1-2-5 step inside
[min, max]: at mostmax_count, at least two, every one a round number at its step’s precision, so an axis reads 0, 10, 20, 30 instead of 0, 11.5, 23, 34.5. A range too narrow or too odd for two round ticks gets its two ends.