Pie & donut
Show positive weights as angular shares, with optional holes and concentric rings.
Try it
These previews run the exact builder functions in the code sample below.
Hover a slice to see its tooltip.
Basic use
PieChartBuilder combines a PiePlot, dataset, and presentation into a GPUI element. A donut uses the same builder with a nonzero .inner_radius(...); there is no separate donut control.
use gpui::{div, prelude::*, px};
use xgpui_luma::controls::tooltip::Tooltip;
use xgpui_luma_plot::{
Dataset, DatasetError, PieChartBuilder, PieLabelPlacement,
PiePresentation, PieRingPresentation, Sample, Series,
};
fn browser_share() -> Result<impl IntoElement, DatasetError> {
let data = Dataset::new().with_series(Series::new("browsers", "Browsers", [
Sample::new(0.0, 275.0), Sample::new(1.0, 200.0), Sample::new(2.0, 125.0),
]))?;
let chart = PieChartBuilder::new(data, "browsers")
.id("browser-share")
.style(PiePresentation::default())
.inner_radius(0.6)
.slice_names(["Chrome", "Safari", "Other"])
.slice_labels(|slice| format!("{:.0}%", slice.fraction * 100.0).into())
.style_override(|style| style.label_placement = PieLabelPlacement::Outside)
.center_text("600", "Visits")
.legend(true)
.slice_tooltip(|slice| Tooltip::new(format!("{}: {}", slice.label, slice.sample.y)))
.build();
Ok(div().w_full().h(px(320.0)).child(chart))
}
fn comparison_rings(data: Dataset) -> impl IntoElement {
let chart = PieChartBuilder::new(data, "current")
.id("comparison-rings")
.slice_names(["Chrome", "Safari", "Other"])
.inner_radius(0.4)
.outer_radius(0.65)
.ring("previous", PieRingPresentation::new(0.72, 1.0))
.build();
div().w_full().h(px(320.0)).child(chart)
}Handle DatasetError in the owning view and render the returned element. For the ring example, supply current and previous series. Give sibling charts distinct IDs.
Data and angles
Y contains weights, not required percentages. Only finite samples with positive Y contribute. Accepted weights are normalized within each pie; original sample indices and order remain intact. X is an optional numeric category coordinate, not an angle.
.slice_names(...) indexes names by original sample position; omitted samples do not shift later names or colors. Missing names fall back to X. .start_angle(...) uses degrees from the rightward ray; positive angles point clockwise on screen. .clockwise(true) changes sector traversal from the default counterclockwise direction.
Donuts and concentric rings
.inner_radius(...) is the hole/outer-radius ratio, clamped to 0–0.95. .outer_radius(...) scales the primary pie against the common chart radius. Added PieRingPresentation bands specify inner/outer ratios against that same common radius.
Each added series is normalized independently. Choose separated bands explicitly; the builder allows overlapping bands. Center text is host-supplied and is not an automatically computed total.
Styling and labels
Bind PiePresentation or a live PieStyleProvider using .style(...), then apply local overrides. Binding a new style clears earlier style overrides, including radius helpers. Palette, per-sample fills, labels, leader lines, legend, active expansion, and empty-state appearance belong to presentation.
Labels are opt-in through .slice_labels(...); choose Inside or Outside placement. .slice_style(index, fill) overrides a source sample’s fill. The built-in legend is hidden unless enabled. See Styling for theme and redraw ownership.
Interaction and motion
.on_slice_event(...) reports PieSliceEvent::HoverChanged and Select with original observations. PieSlice includes the series, sample index, normalized fraction, and ring identity: ring_index is None for the primary pie or the added ring’s zero-based index.
The host owns persistent selection. Pass it back with .active_slice(...), .ring_active_slice(...), or .active_ring_slice(...). Use ring-index selection when the same series is added more than once.
.slice_tooltip(...) supplies SDK tooltips; configure delay on the returned Tooltip. .tooltip_motion(...) and .active_motion(...) configure transitions. .draw_progress(...) reveals sectors in angular order; the host drives replay timing and redraws.