1use super::color::Color;
2use super::font::{FontDesc, FontError, FontFamily, FontStyle, FontTransform};
3use super::size::{HasDimension, SizeDesc};
4use super::BLACK;
5pub use plotters_backend::text_anchor;
6use plotters_backend::{BackendColor, BackendCoord, BackendStyle, BackendTextStyle};
7
8#[derive(Clone)]
10pub struct TextStyle<'a> {
11 pub font: FontDesc<'a>,
13 pub color: BackendColor,
15 pub pos: text_anchor::Pos,
17}
18pub trait IntoTextStyle<'a> {
19 fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>;
20
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
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
46pub 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
53impl<'a, T: IntoTextStyle<'a>> IntoTextStyle<'a> for TextStyleBuilder<'a, T> {
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
66impl<'a> TextStyle<'a> {
67 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 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 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
124impl<'a> IntoTextStyle<'a> for FontDesc<'a> {
125 fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
126 self.into()
127 }
128}
129
130impl<'a> IntoTextStyle<'a> for TextStyle<'a> {
131 fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
132 self
133 }
134}
135
136impl<'a> IntoTextStyle<'a> for &'a str {
137 fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
138 self.into()
139 }
140}
141
142impl<'a> IntoTextStyle<'a> for FontFamily<'a> {
143 fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
144 self.into()
145 }
146}
147
148impl IntoTextStyle<'static> for u32 {
149 fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
150 TextStyle::from((FontFamily::SansSerif, self))
151 }
152}
153
154impl IntoTextStyle<'static> for f64 {
155 fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
156 TextStyle::from((FontFamily::SansSerif, self))
157 }
158}
159
160impl<'a, T: Color> IntoTextStyle<'a> for &'a T {
161 fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
162 TextStyle::from(FontFamily::SansSerif).color(self)
163 }
164}
165
166impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T) {
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
172impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a> for (F, T, &'a C) {
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
178impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T, FontStyle) {
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
184impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a>
185 for (F, T, FontStyle, &'a C)
186{
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
192impl<'a, 'b: 'a> Into<TextStyle<'a>> for &'b TextStyle<'a> {
194 fn into(self) -> TextStyle<'a> {
195 self.clone()
196 }
197}
198
199impl<'a, T: Into<FontDesc<'a>>> From<T> for TextStyle<'a> {
200 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
209impl<'a> BackendTextStyle for TextStyle<'a> {
210 type FontError = FontError;
211 fn color(&self) -> BackendColor {
212 self.color
213 }
214
215 fn size(&self) -> f64 {
216 self.font.get_size()
217 }
218
219 fn transform(&self) -> FontTransform {
220 self.font.get_transform()
221 }
222
223 fn style(&self) -> FontStyle {
224 self.font.get_style()
225 }
226
227 #[allow(clippy::type_complexity)]
228 fn layout_box(&self, text: &str) -> Result<((i32, i32), (i32, i32)), Self::FontError> {
229 self.font.layout_box(text)
230 }
231
232 fn anchor(&self) -> text_anchor::Pos {
233 self.pos
234 }
235
236 fn family(&self) -> FontFamily {
237 self.font.get_family()
238 }
239
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}