1 use super::color::Color;
2 use super::font::{FontDesc, FontError, FontFamily, FontStyle, FontTransform};
3 use super::size::{HasDimension, SizeDesc};
4 use super::BLACK;
5 pub use plotters_backend::text_anchor;
6 use plotters_backend::{BackendColor, BackendCoord, BackendStyle, BackendTextStyle};
7 
8 /// Style of a text
9 #[derive(Clone)]
10 pub struct TextStyle<'a> {
11     /// The font description
12     pub font: FontDesc<'a>,
13     /// The text color
14     pub color: BackendColor,
15     /// The anchor point position
16     pub pos: text_anchor::Pos,
17 }
18 pub trait IntoTextStyle<'a> {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>19     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>;
20 
with_color<C: Color>(self, color: C) -> TextStyleBuilder<'a, Self> where Self: Sized,21     fn with_color<C: Color>(self, color: C) -> TextStyleBuilder<'a, Self>
22     where
23         Self: Sized,
24     {
25         TextStyleBuilder {
26             base: self,
27             new_color: Some(color.to_backend_color()),
28             new_pos: None,
29             _phatom: std::marker::PhantomData,
30         }
31     }
32 
with_anchor<C: Color>(self, pos: text_anchor::Pos) -> TextStyleBuilder<'a, Self> where Self: Sized,33     fn with_anchor<C: Color>(self, pos: text_anchor::Pos) -> TextStyleBuilder<'a, Self>
34     where
35         Self: Sized,
36     {
37         TextStyleBuilder {
38             base: self,
39             new_pos: Some(pos),
40             new_color: None,
41             _phatom: std::marker::PhantomData,
42         }
43     }
44 }
45 
46 pub struct TextStyleBuilder<'a, T: IntoTextStyle<'a>> {
47     base: T,
48     new_color: Option<BackendColor>,
49     new_pos: Option<text_anchor::Pos>,
50     _phatom: std::marker::PhantomData<&'a T>,
51 }
52 
53 impl<'a, T: IntoTextStyle<'a>> IntoTextStyle<'a> for TextStyleBuilder<'a, T> {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>54     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
55         let mut base = self.base.into_text_style(parent);
56         if let Some(color) = self.new_color {
57             base.color = color;
58         }
59         if let Some(pos) = self.new_pos {
60             base = base.pos(pos);
61         }
62         base
63     }
64 }
65 
66 impl<'a> TextStyle<'a> {
67     /// Sets the color of the style.
68     ///
69     /// - `color`: The required color
70     /// - **returns** The up-to-dated text style
71     ///
72     /// ```rust
73     /// use plotters::prelude::*;
74     ///
75     /// let style = TextStyle::from(("sans-serif", 20).into_font()).color(&RED);
76     /// ```
color<C: Color>(&self, color: &'a C) -> Self77     pub fn color<C: Color>(&self, color: &'a C) -> Self {
78         Self {
79             font: self.font.clone(),
80             color: color.to_backend_color(),
81             pos: self.pos,
82         }
83     }
84 
85     /// Sets the font transformation of the style.
86     ///
87     /// - `trans`: The required transformation
88     /// - **returns** The up-to-dated text style
89     ///
90     /// ```rust
91     /// use plotters::prelude::*;
92     ///
93     /// let style = TextStyle::from(("sans-serif", 20).into_font()).transform(FontTransform::Rotate90);
94     /// ```
transform(&self, trans: FontTransform) -> Self95     pub fn transform(&self, trans: FontTransform) -> Self {
96         Self {
97             font: self.font.clone().transform(trans),
98             color: self.color,
99             pos: self.pos,
100         }
101     }
102 
103     /// Sets the anchor position.
104     ///
105     /// - `pos`: The required anchor position
106     /// - **returns** The up-to-dated text style
107     ///
108     /// ```rust
109     /// use plotters::prelude::*;
110     /// use plotters::style::text_anchor::{Pos, HPos, VPos};
111     ///
112     /// let pos = Pos::new(HPos::Left, VPos::Top);
113     /// let style = TextStyle::from(("sans-serif", 20).into_font()).pos(pos);
114     /// ```
pos(&self, pos: text_anchor::Pos) -> Self115     pub fn pos(&self, pos: text_anchor::Pos) -> Self {
116         Self {
117             font: self.font.clone(),
118             color: self.color,
119             pos,
120         }
121     }
122 }
123 
124 impl<'a> IntoTextStyle<'a> for FontDesc<'a> {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>125     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
126         self.into()
127     }
128 }
129 
130 impl<'a> IntoTextStyle<'a> for TextStyle<'a> {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>131     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
132         self
133     }
134 }
135 
136 impl<'a> IntoTextStyle<'a> for &'a str {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>137     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
138         self.into()
139     }
140 }
141 
142 impl<'a> IntoTextStyle<'a> for FontFamily<'a> {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>143     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
144         self.into()
145     }
146 }
147 
148 impl IntoTextStyle<'static> for u32 {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static>149     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
150         TextStyle::from((FontFamily::SansSerif, self))
151     }
152 }
153 
154 impl IntoTextStyle<'static> for f64 {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static>155     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
156         TextStyle::from((FontFamily::SansSerif, self))
157     }
158 }
159 
160 impl<'a, T: Color> IntoTextStyle<'a> for &'a T {
into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a>161     fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
162         TextStyle::from(FontFamily::SansSerif).color(self)
163     }
164 }
165 
166 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T) {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>167     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
168         (self.0.into(), self.1.in_pixels(parent)).into()
169     }
170 }
171 
172 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a> for (F, T, &'a C) {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>173     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
174         IntoTextStyle::into_text_style((self.0, self.1), parent).color(self.2)
175     }
176 }
177 
178 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T, FontStyle) {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>179     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
180         (self.0.into(), self.1.in_pixels(parent), self.2).into()
181     }
182 }
183 
184 impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a>
185     for (F, T, FontStyle, &'a C)
186 {
into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>187     fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
188         IntoTextStyle::into_text_style((self.0, self.1, self.2), parent).color(self.3)
189     }
190 }
191 
192 /// Make sure that we are able to automatically copy the `TextStyle`
193 impl<'a, 'b: 'a> Into<TextStyle<'a>> for &'b TextStyle<'a> {
into(self) -> TextStyle<'a>194     fn into(self) -> TextStyle<'a> {
195         self.clone()
196     }
197 }
198 
199 impl<'a, T: Into<FontDesc<'a>>> From<T> for TextStyle<'a> {
from(font: T) -> Self200     fn from(font: T) -> Self {
201         Self {
202             font: font.into(),
203             color: BLACK.to_backend_color(),
204             pos: text_anchor::Pos::default(),
205         }
206     }
207 }
208 
209 impl<'a> BackendTextStyle for TextStyle<'a> {
210     type FontError = FontError;
color(&self) -> BackendColor211     fn color(&self) -> BackendColor {
212         self.color
213     }
214 
size(&self) -> f64215     fn size(&self) -> f64 {
216         self.font.get_size()
217     }
218 
transform(&self) -> FontTransform219     fn transform(&self) -> FontTransform {
220         self.font.get_transform()
221     }
222 
style(&self) -> FontStyle223     fn style(&self) -> FontStyle {
224         self.font.get_style()
225     }
226 
227     #[allow(clippy::type_complexity)]
layout_box(&self, text: &str) -> Result<((i32, i32), (i32, i32)), Self::FontError>228     fn layout_box(&self, text: &str) -> Result<((i32, i32), (i32, i32)), Self::FontError> {
229         self.font.layout_box(text)
230     }
231 
anchor(&self) -> text_anchor::Pos232     fn anchor(&self) -> text_anchor::Pos {
233         self.pos
234     }
235 
family(&self) -> FontFamily236     fn family(&self) -> FontFamily {
237         self.font.get_family()
238     }
239 
draw<E, DrawFunc: FnMut(i32, i32, BackendColor) -> Result<(), E>>( &self, text: &str, pos: BackendCoord, mut draw: DrawFunc, ) -> Result<Result<(), E>, Self::FontError>240     fn draw<E, DrawFunc: FnMut(i32, i32, BackendColor) -> Result<(), E>>(
241         &self,
242         text: &str,
243         pos: BackendCoord,
244         mut draw: DrawFunc,
245     ) -> Result<Result<(), E>, Self::FontError> {
246         let color = self.color.color();
247         self.font.draw(text, pos, move |x, y, a| {
248             let mix_color = color.mix(a as f64);
249             draw(x, y, mix_color)
250         })
251     }
252 }
253