1use super::ChartContext;
2use crate::coord::CoordTranslate;
3use crate::drawing::DrawingAreaErrorKind;
4use crate::element::{DynElement, EmptyElement, IntoDynElement, MultiLineText, Rectangle};
5use crate::style::{IntoFont, IntoTextStyle, ShapeStyle, SizeDesc, TextStyle, TRANSPARENT};
6
7use plotters_backend::{BackendCoord, DrawingBackend, DrawingErrorKind};
8
9type SeriesAnnoDrawFn<'a, DB> = dyn Fn(BackendCoord) -> DynElement<'a, DB, BackendCoord> + 'a;
10
11pub struct SeriesAnno<'a, DB: DrawingBackend> {
15 label: Option<String>,
16 draw_func: Option<Box<SeriesAnnoDrawFn<'a, DB>>>,
17}
18
19impl<'a, DB: DrawingBackend> SeriesAnno<'a, DB> {
20 #[allow(clippy::option_as_ref_deref)]
21 pub(crate) fn get_label(&self) -> &str {
22 self.label.as_ref().map(|x| x.as_str()).unwrap_or("")
24 }
25
26 pub(crate) fn get_draw_func(&self) -> Option<&SeriesAnnoDrawFn<'a, DB>> {
27 self.draw_func.as_ref().map(|x| x.as_ref())
28 }
29
30 pub(crate) fn new() -> Self {
31 Self {
32 label: None,
33 draw_func: None,
34 }
35 }
36
37 pub fn label<L: Into<String>>(&mut self, label: L) -> &mut Self {
40 self.label = Some(label.into());
41 self
42 }
43
44 pub fn legend<E: IntoDynElement<'a, DB, BackendCoord>, T: Fn(BackendCoord) -> E + 'a>(
49 &mut self,
50 func: T,
51 ) -> &mut Self {
52 self.draw_func = Some(Box::new(move |p| func(p).into_dyn()));
53 self
54 }
55}
56
57pub enum SeriesLabelPosition {
59 UpperLeft,
60 MiddleLeft,
61 LowerLeft,
62 UpperMiddle,
63 MiddleMiddle,
64 LowerMiddle,
65 UpperRight,
66 MiddleRight,
67 LowerRight,
68 Coordinate(i32, i32),
70}
71
72impl SeriesLabelPosition {
73 fn layout_label_area(&self, label_dim: (i32, i32), area_dim: (u32, u32)) -> (i32, i32) {
74 use SeriesLabelPosition::*;
75 (
76 match self {
77 UpperLeft | MiddleLeft | LowerLeft => 5,
78 UpperMiddle | MiddleMiddle | LowerMiddle => {
79 (area_dim.0 as i32 - label_dim.0 as i32) / 2
80 }
81 UpperRight | MiddleRight | LowerRight => area_dim.0 as i32 - label_dim.0 as i32 - 5,
82 Coordinate(x, _) => *x,
83 },
84 match self {
85 UpperLeft | UpperMiddle | UpperRight => 5,
86 MiddleLeft | MiddleMiddle | MiddleRight => {
87 (area_dim.1 as i32 - label_dim.1 as i32) / 2
88 }
89 LowerLeft | LowerMiddle | LowerRight => area_dim.1 as i32 - label_dim.1 as i32 - 5,
90 Coordinate(_, y) => *y,
91 },
92 )
93 }
94}
95
96pub struct SeriesLabelStyle<'a, 'b, DB: DrawingBackend, CT: CoordTranslate> {
98 target: &'b mut ChartContext<'a, DB, CT>,
99 position: SeriesLabelPosition,
100 legend_area_size: u32,
101 border_style: ShapeStyle,
102 background: ShapeStyle,
103 label_font: Option<TextStyle<'b>>,
104 margin: u32,
105}
106
107impl<'a, 'b, DB: DrawingBackend + 'a, CT: CoordTranslate> SeriesLabelStyle<'a, 'b, DB, CT> {
108 pub(super) fn new(target: &'b mut ChartContext<'a, DB, CT>) -> Self {
109 Self {
110 target,
111 position: SeriesLabelPosition::MiddleRight,
112 legend_area_size: 30,
113 border_style: (&TRANSPARENT).into(),
114 background: (&TRANSPARENT).into(),
115 label_font: None,
116 margin: 10,
117 }
118 }
119
120 pub fn position(&mut self, pos: SeriesLabelPosition) -> &mut Self {
123 self.position = pos;
124 self
125 }
126
127 pub fn margin<S: SizeDesc>(&mut self, value: S) -> &mut Self {
131 self.margin = value
132 .in_pixels(&self.target.plotting_area().dim_in_pixel())
133 .max(0) as u32;
134 self
135 }
136
137 pub fn legend_area_size<S: SizeDesc>(&mut self, size: S) -> &mut Self {
140 let size = size
141 .in_pixels(&self.target.plotting_area().dim_in_pixel())
142 .max(0) as u32;
143 self.legend_area_size = size;
144 self
145 }
146
147 pub fn border_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
150 self.border_style = style.into();
151 self
152 }
153
154 pub fn background_style<S: Into<ShapeStyle>>(&mut self, style: S) -> &mut Self {
157 self.background = style.into();
158 self
159 }
160
161 pub fn label_font<F: IntoTextStyle<'b>>(&mut self, font: F) -> &mut Self {
164 self.label_font = Some(font.into_text_style(&self.target.plotting_area().dim_in_pixel()));
165 self
166 }
167
168 pub fn draw(&mut self) -> Result<(), DrawingAreaErrorKind<DB::ErrorType>> {
170 let drawing_area = self.target.plotting_area().strip_coord_spec();
171
172 let default_font = ("sans-serif", 12).into_font();
175 let default_style: TextStyle = default_font.into();
176
177 let font = {
178 let mut temp = None;
179 std::mem::swap(&mut self.label_font, &mut temp);
180 temp.unwrap_or(default_style)
181 };
182
183 let mut label_element = MultiLineText::<_, &str>::new((0, 0), &font);
184 let mut funcs = vec![];
185
186 for anno in self.target.series_anno.iter() {
187 let label_text = anno.get_label();
188 let draw_func = anno.get_draw_func();
189
190 if label_text == "" && draw_func.is_none() {
191 continue;
192 }
193
194 funcs.push(
195 draw_func.unwrap_or_else(|| &|p: BackendCoord| EmptyElement::at(p).into_dyn()),
196 );
197 label_element.push_line(label_text);
198 }
199
200 let (mut w, mut h) = label_element.estimate_dimension().map_err(|e| {
201 DrawingAreaErrorKind::BackendError(DrawingErrorKind::FontError(Box::new(e)))
202 })?;
203
204 let margin = self.margin as i32;
205
206 w += self.legend_area_size as i32 + margin * 2;
207 h += margin * 2;
208
209 let (area_w, area_h) = drawing_area.dim_in_pixel();
210
211 let (label_x, label_y) = self.position.layout_label_area((w, h), (area_w, area_h));
212
213 label_element.relocate((
214 label_x + self.legend_area_size as i32 + margin,
215 label_y + margin,
216 ));
217
218 drawing_area.draw(&Rectangle::new(
219 [(label_x, label_y), (label_x + w, label_y + h)],
220 self.background.filled(),
221 ))?;
222 drawing_area.draw(&Rectangle::new(
223 [(label_x, label_y), (label_x + w, label_y + h)],
224 self.border_style.clone(),
225 ))?;
226 drawing_area.draw(&label_element)?;
227
228 for (((_, y0), (_, y1)), make_elem) in label_element
229 .compute_line_layout()
230 .map_err(|e| {
231 DrawingAreaErrorKind::BackendError(DrawingErrorKind::FontError(Box::new(e)))
232 })?
233 .into_iter()
234 .zip(funcs.into_iter())
235 {
236 let legend_element = make_elem((label_x + margin, (y0 + y1) / 2));
237 drawing_area.draw(&legend_element)?;
238 }
239
240 Ok(())
241 }
242}