blob: 374a5ce8c71b07c6315319be83bfc32d95b41ab8 [file] [log] [blame]
use super::color::Color;
use super::font::{FontDesc, FontError, FontFamily, FontStyle, FontTransform};
use super::size::{HasDimension, SizeDesc};
use super::BLACK;
pub use plotters_backend::text_anchor;
use plotters_backend::{BackendColor, BackendCoord, BackendStyle, BackendTextStyle};
/// Style of a text
#[derive(Clone)]
pub struct TextStyle<'a> {
/// The font description
pub font: FontDesc<'a>,
/// The text color
pub color: BackendColor,
/// The anchor point position
pub pos: text_anchor::Pos,
}
pub trait IntoTextStyle<'a> {
fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a>;
fn with_color<C: Color>(self, color: C) -> TextStyleBuilder<'a, Self>
where
Self: Sized,
{
TextStyleBuilder {
base: self,
new_color: Some(color.to_backend_color()),
new_pos: None,
_phatom: std::marker::PhantomData,
}
}
fn with_anchor<C: Color>(self, pos: text_anchor::Pos) -> TextStyleBuilder<'a, Self>
where
Self: Sized,
{
TextStyleBuilder {
base: self,
new_pos: Some(pos),
new_color: None,
_phatom: std::marker::PhantomData,
}
}
}
pub struct TextStyleBuilder<'a, T: IntoTextStyle<'a>> {
base: T,
new_color: Option<BackendColor>,
new_pos: Option<text_anchor::Pos>,
_phatom: std::marker::PhantomData<&'a T>,
}
impl<'a, T: IntoTextStyle<'a>> IntoTextStyle<'a> for TextStyleBuilder<'a, T> {
fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
let mut base = self.base.into_text_style(parent);
if let Some(color) = self.new_color {
base.color = color;
}
if let Some(pos) = self.new_pos {
base = base.pos(pos);
}
base
}
}
impl<'a> TextStyle<'a> {
/// Sets the color of the style.
///
/// - `color`: The required color
/// - **returns** The up-to-dated text style
///
/// ```rust
/// use plotters::prelude::*;
///
/// let style = TextStyle::from(("sans-serif", 20).into_font()).color(&RED);
/// ```
pub fn color<C: Color>(&self, color: &'a C) -> Self {
Self {
font: self.font.clone(),
color: color.to_backend_color(),
pos: self.pos,
}
}
/// Sets the font transformation of the style.
///
/// - `trans`: The required transformation
/// - **returns** The up-to-dated text style
///
/// ```rust
/// use plotters::prelude::*;
///
/// let style = TextStyle::from(("sans-serif", 20).into_font()).transform(FontTransform::Rotate90);
/// ```
pub fn transform(&self, trans: FontTransform) -> Self {
Self {
font: self.font.clone().transform(trans),
color: self.color,
pos: self.pos,
}
}
/// Sets the anchor position.
///
/// - `pos`: The required anchor position
/// - **returns** The up-to-dated text style
///
/// ```rust
/// use plotters::prelude::*;
/// use plotters::style::text_anchor::{Pos, HPos, VPos};
///
/// let pos = Pos::new(HPos::Left, VPos::Top);
/// let style = TextStyle::from(("sans-serif", 20).into_font()).pos(pos);
/// ```
pub fn pos(&self, pos: text_anchor::Pos) -> Self {
Self {
font: self.font.clone(),
color: self.color,
pos,
}
}
}
impl<'a> IntoTextStyle<'a> for FontDesc<'a> {
fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
self.into()
}
}
impl<'a> IntoTextStyle<'a> for TextStyle<'a> {
fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
self
}
}
impl<'a> IntoTextStyle<'a> for &'a str {
fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
self.into()
}
}
impl<'a> IntoTextStyle<'a> for FontFamily<'a> {
fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
self.into()
}
}
impl IntoTextStyle<'static> for u32 {
fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
TextStyle::from((FontFamily::SansSerif, self))
}
}
impl IntoTextStyle<'static> for f64 {
fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'static> {
TextStyle::from((FontFamily::SansSerif, self))
}
}
impl<'a, T: Color> IntoTextStyle<'a> for &'a T {
fn into_text_style<P: HasDimension>(self, _: &P) -> TextStyle<'a> {
TextStyle::from(FontFamily::SansSerif).color(self)
}
}
impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T) {
fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
(self.0.into(), self.1.in_pixels(parent)).into()
}
}
impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a> for (F, T, &'a C) {
fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
IntoTextStyle::into_text_style((self.0, self.1), parent).color(self.2)
}
}
impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc> IntoTextStyle<'a> for (F, T, FontStyle) {
fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
(self.0.into(), self.1.in_pixels(parent), self.2).into()
}
}
impl<'a, F: Into<FontFamily<'a>>, T: SizeDesc, C: Color> IntoTextStyle<'a>
for (F, T, FontStyle, &'a C)
{
fn into_text_style<P: HasDimension>(self, parent: &P) -> TextStyle<'a> {
IntoTextStyle::into_text_style((self.0, self.1, self.2), parent).color(self.3)
}
}
/// Make sure that we are able to automatically copy the `TextStyle`
impl<'a, 'b: 'a> Into<TextStyle<'a>> for &'b TextStyle<'a> {
fn into(self) -> TextStyle<'a> {
self.clone()
}
}
impl<'a, T: Into<FontDesc<'a>>> From<T> for TextStyle<'a> {
fn from(font: T) -> Self {
Self {
font: font.into(),
color: BLACK.to_backend_color(),
pos: text_anchor::Pos::default(),
}
}
}
impl<'a> BackendTextStyle for TextStyle<'a> {
type FontError = FontError;
fn color(&self) -> BackendColor {
self.color
}
fn size(&self) -> f64 {
self.font.get_size()
}
fn transform(&self) -> FontTransform {
self.font.get_transform()
}
fn style(&self) -> FontStyle {
self.font.get_style()
}
#[allow(clippy::type_complexity)]
fn layout_box(&self, text: &str) -> Result<((i32, i32), (i32, i32)), Self::FontError> {
self.font.layout_box(text)
}
fn anchor(&self) -> text_anchor::Pos {
self.pos
}
fn family(&self) -> FontFamily {
self.font.get_family()
}
fn draw<E, DrawFunc: FnMut(i32, i32, BackendColor) -> Result<(), E>>(
&self,
text: &str,
pos: BackendCoord,
mut draw: DrawFunc,
) -> Result<Result<(), E>, Self::FontError> {
let color = self.color.color();
self.font.draw(text, pos, move |x, y, a| {
let mix_color = color.mix(a as f64);
draw(x, y, mix_color)
})
}
}