Skip to content

Panes

Panes are stacked plots inside one Chart. They share the time axis, horizontal navigation and crosshair time, while each pane has its own price scale. A multi-chart grid contains separate Chart instances, each with its own data and viewport; see synchronizing charts.

This scene hides the built-in volume overlay with ChartConfig::show_volume and adds the first chart’s synthetic volume as a Columns series in a lower pane. Move to main puts it beside the candles on an independent scale; Move below returns it. Drag the separator, double-click it to reset, try Lower pane: 140 pt, or restore the 3:1 proportions. Create Panes::new(&mut chart) once, then call its show(ui, &mut chart) before chart.show(ui) on each frame.

One chart with two panesOpen full size

Move the volume series between panes and change their relative heights.

The interactive example needs JavaScript and WebAssembly. You can still use the chart setup alongside it.

panes.rs
use eframe::egui;
use egui_charts::{
Chart,
model::{Bar, ChartType, SeriesScale},
widget::{PaneId, SeriesId, SeriesOptions},
};
/// Move an added series while keeping an empty destination available.
pub struct Panes {
series: SeriesId,
pane: PaneId,
}
impl Panes {
/// Put host-supplied sample volume in a pane below the price series.
pub fn new(chart: &mut Chart) -> Self {
chart.config_mut().show_volume = false;
let bars = chart
.bars(SeriesId::MAIN)
.iter()
.map(|bar| Bar::from_value(bar.time, bar.volume))
.collect();
let series = chart.add_series(SeriesOptions {
name: "SAMPLE VOLUME".into(),
chart_type: ChartType::Columns,
columns_base: Some(0.0),
scale: SeriesScale::Separate(1),
..Default::default()
});
chart.set_data(series, bars);
let pane = chart.add_pane();
chart.set_preserve_empty_pane(pane, true);
chart.move_series_to_pane(series, pane);
chart.set_pane_stretch_factor(PaneId::MAIN, 3.0);
chart.set_pane_stretch_factor(pane, 1.0);
Self { series, pane }
}
/// Try placement, fixed height and proportional sizing on one shared time axis.
pub fn show(&mut self, ui: &mut egui::Ui, chart: &mut Chart) {
ui.horizontal_wrapped(|ui| {
let in_main = chart.series_pane(self.series) == Some(PaneId::MAIN);
if ui
.button(if in_main {
"Move below"
} else {
"Move to main"
})
.clicked()
{
chart.move_series_to_pane(
self.series,
if in_main { self.pane } else { PaneId::MAIN },
);
}
if ui.button("Lower pane: 140 pt").clicked() {
chart.set_pane_height(self.pane, 140.0);
}
if ui.button("Restore 3:1").clicked() {
chart.set_pane_stretch_factor(PaneId::MAIN, 3.0);
chart.set_pane_stretch_factor(self.pane, 1.0);
}
ui.checkbox(&mut chart.pane_options_mut().resizable, "Resizable");
});
ui.label("Drag the separator; double-click it to reset proportions.");
if let Some(height) = chart.pane_height(self.pane) {
ui.label(format!("Lower pane last drawn: {height:.0} pt"));
}
}
}

Keep the SeriesId returned by add_series, then create a destination with add_pane. move_series_to_pane accepts an added series or study and its destination PaneId. It returns false for the main series, unknown IDs or a custom study that rejects the placement. Use PaneId::MAIN to move an added series back.

In the main pane, the added series follows SeriesOptions::scale. In a lower pane, it uses that pane’s scale. This is why the scene assigns volume an independent scale for its return to the main pane. Change its active scale through series_price_scale_mut, as in the price scale guide.

An empty lower pane is normally removed when its last member leaves. The scene calls set_preserve_empty_pane so the same destination remains available. Inspect placement with panes, pane_series and series_pane. remove_pane also removes the series and studies it contains; move them out first to keep them. The main pane cannot be removed.

set_pane_stretch_factor divides the available plot height proportionally: factors 3 and 1 give the main pane three quarters and the lower pane one quarter, subject to the 40-point minimum for each expanded pane. Set finite, positive factors. These proportions survive a container resize.

set_pane_height requests a height in egui points on the next layout, then converts it to a stretch factor. It is not a permanent fixed pixel height. The other expanded panes share the remaining space and all keep their minimum. Read pane_height after show; it returns None for a pane that has not been drawn or is hidden. Read the resulting proportions with pane_stretch_factor.

PaneOptions::resizable enables separator dragging and double-click reset by default. Change it through pane_options_mut. separator_color and separator_hover_color override the theme colours. Use move_pane or swap_panes to change vertical order without moving the data into another chart.