1use esoc_gfx::canvas::Canvas;
5use esoc_gfx::color::Color;
6use esoc_gfx::element::{DrawElement, Element};
7use esoc_gfx::geom::Rect;
8use esoc_gfx::layer::Layer;
9use esoc_gfx::style::{Fill, FontStyle, Stroke, TextAnchor};
10
11use crate::theme::Theme;
12
13#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
15pub enum LegendPosition {
16 #[default]
18 UpperRight,
19 UpperLeft,
21 LowerRight,
23 LowerLeft,
25}
26
27#[derive(Clone, Debug)]
29pub struct LegendEntry {
30 pub label: String,
32 pub color: Color,
34}
35
36pub fn render_legend(
38 canvas: &mut Canvas,
39 plot_area: Rect,
40 entries: &[LegendEntry],
41 position: LegendPosition,
42 theme: &Theme,
43) {
44 if entries.is_empty() {
45 return;
46 }
47
48 let row_height = theme.legend_font_size * 1.6;
49 let swatch_size = theme.legend_font_size * 0.8;
50 let padding = 8.0;
51 let gap = 6.0;
52
53 let text_measurer = esoc_gfx::text::HeuristicTextMeasurer;
55 let max_label_width = entries
56 .iter()
57 .map(|e| {
58 esoc_gfx::text::TextMeasurer::measure_width(
59 &text_measurer,
60 &e.label,
61 theme.legend_font_size,
62 )
63 })
64 .fold(0.0_f64, f64::max);
65
66 let box_width = padding * 2.0 + swatch_size + gap + max_label_width;
67 let box_height = padding * 2.0 + entries.len() as f64 * row_height;
68
69 let (bx, by) = match position {
71 LegendPosition::UpperRight => (plot_area.right() - box_width - 10.0, plot_area.y + 10.0),
72 LegendPosition::UpperLeft => (plot_area.x + 10.0, plot_area.y + 10.0),
73 LegendPosition::LowerRight => (
74 plot_area.right() - box_width - 10.0,
75 plot_area.bottom() - box_height - 10.0,
76 ),
77 LegendPosition::LowerLeft => (plot_area.x + 10.0, plot_area.bottom() - box_height - 10.0),
78 };
79
80 canvas.add(DrawElement::new(
82 Element::Rect {
83 rect: Rect::new(bx, by, box_width, box_height),
84 fill: Fill::Solid(theme.background.with_alpha(0.9)),
85 stroke: Some(Stroke::solid(theme.grid_color, 0.5)),
86 rx: 3.0,
87 },
88 Layer::Legend,
89 ));
90
91 for (i, entry) in entries.iter().enumerate() {
93 let ey = by + padding + i as f64 * row_height + row_height / 2.0;
94
95 canvas.add(DrawElement::new(
97 Element::Rect {
98 rect: Rect::new(
99 bx + padding,
100 ey - swatch_size / 2.0,
101 swatch_size,
102 swatch_size,
103 ),
104 fill: Fill::Solid(entry.color),
105 stroke: None,
106 rx: 2.0,
107 },
108 Layer::Legend,
109 ));
110
111 let font = FontStyle {
113 family: theme.font_family.clone(),
114 size: theme.legend_font_size,
115 weight: 400,
116 color: theme.foreground,
117 anchor: TextAnchor::Start,
118 };
119 canvas.add(DrawElement::new(
120 Element::text(
121 bx + padding + swatch_size + gap,
122 ey + theme.legend_font_size * 0.35,
123 &entry.label,
124 font,
125 ),
126 Layer::Legend,
127 ));
128 }
129}