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
143
144
145
146
147
//! This crate defines a text displayable.
//! A text displayable profiles a block of text to be displayed onto a framebuffer.

#![no_std]

extern crate alloc;
extern crate displayable;
extern crate font;
extern crate framebuffer;
extern crate framebuffer_printer;
extern crate shapes;
extern crate color;

use alloc::string::String;
use displayable::{Displayable};
use font::{CHARACTER_HEIGHT, CHARACTER_WIDTH};
use framebuffer::{Pixel, Framebuffer};
use color::Color;
use shapes::{Coord, Rectangle};


/// A text displayable profiles the size and color of a block of text. It can display in a framebuffer.
#[derive(Debug)]
pub struct TextDisplay {
    width: usize,
    height: usize,
    /// The position where the next character will be displayed. 
    /// This is updated after each `display()` invocation, and is useful for optimization.
    next_col: usize,
    next_line: usize,
    text: String,
    fg_color: Color,
    bg_color: Color,
    /// The cache of the text that was last displayed.
    cache: String,
}

impl Displayable for TextDisplay {
    fn display<P: Pixel+ From<Color>> (
        &mut self,
        coordinate: Coord,
        framebuffer: &mut Framebuffer<P>,
    ) -> Result<Rectangle, &'static str> {
        let (string, col, line) = if !self.cache.is_empty() && self.text.starts_with(self.cache.as_str()) {
            (
                &self.text.as_str()[self.cache.len()..self.text.len()],
                self.next_col,
                self.next_line,
            )
        } else {
            (self.text.as_str(), 0, 0)
        };

        let (next_col, next_line, mut bounding_box) = framebuffer_printer::print_string(
            framebuffer,
            coordinate,
            self.width,
            self.height,
            string,
            self.fg_color.into(),
            self.bg_color.into(),
            col,
            line,
        );

        if next_line < self.next_line {
            bounding_box.bottom_right.y = ((self.next_line + 1 ) * CHARACTER_HEIGHT) as isize
        }

        self.next_col = next_col;
        self.next_line = next_line;
        self.cache = self.text.clone();

        Ok(bounding_box + coordinate)
    }

    fn set_size(&mut self, width: usize, height: usize) {
        self.width = width;
        self.height = height;
    }

    fn get_size(&self) -> (usize, usize) {
        (self.width, self.height)
    }
}

impl TextDisplay {
    /// Creates a new text displayable.
    /// # Arguments
    /// * `width`, `height`: the dimensions of the text area, in number of characters.
    /// * `fg_color`, `bg_color`: the color of the text and the background behind the text, respectively.
    pub fn new(
        width: usize,
        height: usize,
        fg_color: Color,
        bg_color: Color,
    ) -> Result<TextDisplay, &'static str> {
        Ok(TextDisplay {
            width,
            height,
            next_col: 0,
            next_line: 0,
            text: String::new(),
            fg_color,
            bg_color,
            cache: String::new(),
        })
    }

    /// Gets the background color of the text area
    pub fn get_bg_color(&self) -> Color {
        self.bg_color
    }
    
    /// Clear the cache of the text displayable.
    pub fn reset_cache(&mut self) {
        self.cache = String::new();
    }

    /// Translate the index of a character in the text to the location of the text displayable. Return (column, line).
    pub fn get_location(&self, index: usize) -> (usize, usize) {
        let text_width = self.width / CHARACTER_WIDTH;
        (index % text_width, index / text_width)
    }

    /// Translate the location of a character to its index in the text.
    pub fn get_index(&self, column: usize, line: usize) -> usize {
        let text_width = self.width / CHARACTER_WIDTH;
        line * text_width + column
    }

    /// Gets the size of a text displayable in number of characters.
    pub fn get_dimensions(&self) -> (usize, usize) {
        (self.width / CHARACTER_WIDTH, self.height / CHARACTER_HEIGHT)
    }

    /// Gets the index of next character to be displayabled. It is the position next to existing printed characters in the text displayable.
    pub fn get_next_index(&self) -> usize {
        let col_num = self.width / CHARACTER_WIDTH;
        self.next_line * col_num + self.next_col
    }

    /// Sets the text of the text displayable
    pub fn set_text(&mut self, text: &str) {
        self.text = String::from(text);
    }
}