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