1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use super::font::Font;
use crate::graphics::drawable::Drawable;
use crate::graphics::canvas::Canvas;
use crate::graphics::color::Color;
use wasm_bindgen::JsValue;

const PX_STR: &str = "px";

/// A struct representing the style of a [Text](struct.Text.html).
pub struct TextStyle {
    /// Italic
    pub italic: bool,
    /// Bold
    pub bold: bool,
    /// Underlined is not supported for now!
    pub underlined: bool,
    /// The color of the text
    pub color: Color
}

impl Default for TextStyle {
    fn default() -> TextStyle {
        TextStyle {
            italic: false,
            bold: false,
            underlined: false,
            color: Color::black()
        }
    }
}

/// A text drawable on a [Canvas](../canvas/struct.Canvas.html).
/// Multiline (\n) works only when using a character size in px.
pub struct Text<'a> {
    /// The coords of the text in px.
    pub coords: (usize, usize),
    /// The [font](../font/struct.Font.html) of the text.
    pub font: &'a Font,
    /// The text.
    pub text: String,
    /// The [style](struct.TextStyle.html) of the text (bold/italic...)
    pub style: TextStyle,
    /// The character_size. example: (14, "px")
    pub character_size: (usize, &'a str)
}

impl<'a> Text<'a> {
    /// Create a new text with default values.
    pub fn new(font: &'a Font) -> Text<'a> {
        Text {
            coords: (0,0),
            font,
            text: String::new(),
            style: TextStyle::default(),
            character_size: (26, PX_STR)
        }
    }

    /// Create a new text with some default values.
    pub fn new_with_text_and_coords(font: &'a Font, text: String, coords: (usize, usize)) -> Text<'a> {
        Text {
            coords,
            font,
            text,
            style: TextStyle::default(),
            character_size: (26, PX_STR)
        }
    }

    /// Create a new text with no default value.
    pub fn new_with_options(font: &'a Font, text: String, coords: (usize, usize), style: TextStyle, character_size: (usize, &'a str)) -> Text<'a> {
        Text {
            coords,
            font,
            text,
            style,
            character_size
        }
    }

    /// Set the displayed text.
    pub fn set_text(&mut self, text: String) {
        self.text = text;
    }

    /// Set the [style](struct.TextStyle.html) of the text.
    pub fn set_style(&mut self, style: TextStyle) {
        self.style = style;
    }

    /// Get the width of the text
    /// Needs a mutable reference to a canvas
    pub fn get_width(&self, mut canvas: &mut Canvas) -> f64 {
        self.apply_style_on_canvas(&mut canvas);
        let mut max_width = 0.0;
        for text in self.text.split('\n') {
            let width = canvas.context.measure_text(&text).unwrap().width();
            if width > max_width {
                max_width = width;
            }
        }
        max_width
    }

    /// Get the width of the text.
    /// Works only for font size specified using a value in px.
    pub fn get_height(&self) -> usize {
        self.text.split('\n').count() * self.character_size.0
    }

    fn apply_style_on_canvas(&self, canvas: &mut Canvas) {
        let mut font = String::new();
        if self.style.italic {
            font.push_str("italic ");
        }
        if self.style.bold {
            font.push_str("bold ");
        }
        if self.style.underlined {
            unimplemented!("avoid underlined text for now");
        }
        font.push_str(&self.character_size.0.to_string());
        font.push_str(self.character_size.1);
        font.push_str(" '");
        font.push_str(&self.font.name);
        font.push_str("'");

        canvas.context.set_font(&font);
        canvas.context.set_fill_style(&JsValue::from_str(&self.style.color.to_string()));
    }
}

impl<'a> Drawable for Text<'a> {
    fn draw_on_canvas(&self, mut canvas: &mut Canvas) {
        self.apply_style_on_canvas(&mut canvas);
        for (idx, text) in self.text.split('\n').enumerate() {
            let mut coords = self.coords;
            coords.1 += self.character_size.0 * idx;
            canvas.fill_text(coords, text, None);
        }
    }
}