use crate::axes::AxisScale;
#[allow(deprecated)]
use crate::core::Position;
use crate::core::{GridStyle, LegendPosition};
use super::{LegendConfig, TickConfig};
#[derive(Clone, Debug)]
pub struct LayoutManager {
pub(crate) legend: LegendConfig,
pub(crate) grid_style: GridStyle,
pub(crate) tick_config: TickConfig,
pub(crate) margin: Option<f32>,
pub(crate) scientific_notation: bool,
pub(crate) x_limits: Option<(f64, f64)>,
pub(crate) y_limits: Option<(f64, f64)>,
pub(crate) x_scale: AxisScale,
pub(crate) y_scale: AxisScale,
}
impl Default for LayoutManager {
fn default() -> Self {
Self::new()
}
}
impl LayoutManager {
pub fn new() -> Self {
Self {
legend: LegendConfig::default(),
grid_style: GridStyle::default(),
tick_config: TickConfig::default(),
margin: None,
scientific_notation: false,
x_limits: None,
y_limits: None,
x_scale: AxisScale::Linear,
y_scale: AxisScale::Linear,
}
}
pub fn enable_legend(&mut self, enabled: bool) {
self.legend.enabled = enabled;
}
pub fn legend_enabled(&self) -> bool {
self.legend.enabled
}
pub fn set_legend_position(&mut self, position: impl Into<LegendPosition>) {
self.legend.position = position.into();
}
#[deprecated(
since = "0.6.0",
note = "lossy: the four `Outside*` placements all collapse to `Position::TopRight`. Use `legend_position_full()`, which returns `LegendPosition`"
)]
#[allow(deprecated)]
pub fn legend_position(&self) -> Position {
match self.legend.position {
LegendPosition::Best => Position::Best,
LegendPosition::UpperRight | LegendPosition::Right => Position::TopRight,
LegendPosition::UpperLeft => Position::TopLeft,
LegendPosition::LowerLeft => Position::BottomLeft,
LegendPosition::LowerRight => Position::BottomRight,
LegendPosition::CenterLeft => Position::CenterLeft,
LegendPosition::CenterRight => Position::CenterRight,
LegendPosition::LowerCenter => Position::BottomCenter,
LegendPosition::UpperCenter => Position::TopCenter,
LegendPosition::Center => Position::Center,
LegendPosition::Custom { x, y, .. } => Position::Custom { x, y: 1.0 - y },
LegendPosition::OutsideRight
| LegendPosition::OutsideLeft
| LegendPosition::OutsideUpper
| LegendPosition::OutsideLower => Position::TopRight,
}
}
pub fn set_legend_position_full(&mut self, position: LegendPosition) {
self.legend.position = position;
}
pub fn legend_position_full(&self) -> LegendPosition {
self.legend.position
}
pub fn set_legend_font_size(&mut self, size: f32) {
self.legend.font_size = Some(size);
}
pub fn set_legend_corner_radius(&mut self, radius: f32) {
self.legend.corner_radius = Some(radius);
}
pub fn set_legend_columns(&mut self, cols: usize) {
self.legend.columns = Some(cols);
}
pub fn set_grid_style(&mut self, style: GridStyle) {
self.grid_style = style;
}
pub fn grid_style(&self) -> &GridStyle {
&self.grid_style
}
pub fn grid_style_mut(&mut self) -> &mut GridStyle {
&mut self.grid_style
}
pub fn set_xlim(&mut self, min: f64, max: f64) {
self.x_limits = Some((min, max));
}
pub fn set_ylim(&mut self, min: f64, max: f64) {
self.y_limits = Some((min, max));
}
pub fn xlim(&self) -> Option<(f64, f64)> {
self.x_limits
}
pub fn ylim(&self) -> Option<(f64, f64)> {
self.y_limits
}
pub fn set_x_scale(&mut self, scale: AxisScale) {
self.x_scale = scale;
}
pub fn set_y_scale(&mut self, scale: AxisScale) {
self.y_scale = scale;
}
pub fn x_scale(&self) -> &AxisScale {
&self.x_scale
}
pub fn y_scale(&self) -> &AxisScale {
&self.y_scale
}
pub fn set_margin(&mut self, margin: f32) {
self.margin = Some(margin);
}
pub fn margin(&self) -> Option<f32> {
self.margin
}
pub fn set_scientific_notation(&mut self, enabled: bool) {
self.scientific_notation = enabled;
}
pub fn scientific_notation(&self) -> bool {
self.scientific_notation
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_layout_manager() {
let layout = LayoutManager::new();
assert!(!layout.legend_enabled());
assert!(layout.xlim().is_none());
assert!(layout.ylim().is_none());
assert!(layout.margin().is_none());
assert!(!layout.scientific_notation());
}
#[test]
fn test_axis_limits() {
let mut layout = LayoutManager::new();
layout.set_xlim(0.0, 100.0);
layout.set_ylim(-50.0, 50.0);
assert_eq!(layout.xlim(), Some((0.0, 100.0)));
assert_eq!(layout.ylim(), Some((-50.0, 50.0)));
}
#[test]
fn test_legend_config() {
let mut layout = LayoutManager::new();
layout.enable_legend(true);
layout.set_legend_position(LegendPosition::LowerLeft);
layout.set_legend_font_size(12.0);
assert!(layout.legend_enabled());
assert_eq!(layout.legend_position_full(), LegendPosition::LowerLeft);
assert_eq!(layout.legend.font_size, Some(12.0));
}
#[test]
fn test_full_legend_position_preserves_outside_variants() {
let mut layout = LayoutManager::new();
for position in [
LegendPosition::OutsideRight,
LegendPosition::OutsideLeft,
LegendPosition::OutsideUpper,
LegendPosition::OutsideLower,
] {
layout.set_legend_position_full(position);
assert_eq!(layout.legend_position_full(), position);
}
layout.set_legend_position(LegendPosition::LowerCenter);
assert_eq!(layout.legend_position_full(), LegendPosition::LowerCenter);
}
#[test]
fn test_axis_scales() {
let mut layout = LayoutManager::new();
layout.set_x_scale(AxisScale::Log);
layout.set_y_scale(AxisScale::SymLog { linthresh: 1.0 });
assert!(matches!(layout.x_scale(), AxisScale::Log));
assert!(matches!(layout.y_scale(), AxisScale::SymLog { .. }));
}
}