Add a Grid

The grid holds the state of the terminal with row-major ordering.
Eventually, the grid::Cell type will hold other attributes such as
color, background color, decorations, and weight.

An initialization list is added for common ASCII symbols.
This commit is contained in:
Joe Wilm 2016-04-10 16:19:39 -07:00
parent 99474ef78a
commit b84eb9e921
No known key found for this signature in database
GPG Key ID: 39B57C6972F518DA
3 changed files with 159 additions and 9 deletions

95
src/grid.rs Normal file
View File

@ -0,0 +1,95 @@
//! Functions for computing properties of the terminal grid
use std::ops::{Index, IndexMut};
/// Calculate the number of cells for an axis
pub fn num_cells_axis(cell_width: u32, cell_sep: i32, screen_width: u32) -> u32 {
((screen_width as i32 + cell_sep) as f64 / (cell_width as i32 - cell_sep) as f64) as u32
}
#[derive(Clone)]
pub struct Cell {
pub character: Option<String>,
}
impl Cell {
pub fn new(c: Option<String>) -> Cell {
Cell {
character: c,
}
}
}
/// Represents the terminal display contents
pub struct Grid {
/// Rows in the grid. Each row holds a list of cells corresponding to the columns in that row.
raw: Vec<Row>,
/// Number of columns
_cols: usize,
/// Number of rows.
///
/// Invariant: rows is equivalent to cells.len()
rows: usize,
}
impl Grid {
pub fn new(rows: usize, cols: usize) -> Grid {
let mut raw = Vec::with_capacity(rows);
for _ in 0..raw.capacity() {
raw.push(Row::new(cols));
}
Grid {
raw: raw,
_cols: cols,
rows: rows,
}
}
pub fn rows(&self) -> usize {
self.rows
}
}
impl Index<usize> for Grid {
type Output = Row;
fn index<'a>(&'a self, index: usize) -> &'a Row {
&self.raw[index]
}
}
impl IndexMut<usize> for Grid {
fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Row {
&mut self.raw[index]
}
}
/// A row in the grid
pub struct Row(Vec<Cell>);
impl Row {
pub fn new(columns: usize) -> Row {
Row(vec![Cell::new(None); columns])
}
pub fn cols(&self) -> usize {
self.0.len()
}
}
impl Index<usize> for Row {
type Output = Cell;
fn index<'a>(&'a self, index: usize) -> &'a Cell {
&self.0[index]
}
}
impl IndexMut<usize> for Row {
fn index_mut<'a>(&'a mut self, index: usize) -> &'a mut Cell {
&mut self.0[index]
}
}

View File

@ -6,13 +6,21 @@ extern crate gl;
extern crate cgmath;
extern crate euclid;
use std::collections::HashMap;
mod list_fonts;
mod text;
mod renderer;
mod grid;
use renderer::{Glyph, QuadRenderer};
use text::FontDesc;
use grid::Grid;
static INIT_LIST: &'static str = "abcdefghijklmnopqrstuvwxyz\
ABCDEFGHIJKLMNOPQRSTUVWXYZ\
01234567890\
~`!@#$%^&*()[]{}-_=+\\|\"/?.,<>";
fn main() {
let window = glutin::Window::new().unwrap();
@ -29,13 +37,34 @@ fn main() {
let (dpi_x, dpi_y) = window.get_dpi().unwrap();
let dpr = window.hidpi_factor();
let font_size = 12.0;
let sep_x = 2;
let sep_y = 2;
let desc = FontDesc::new("Ubuntu Mono", "Regular");
let mut rasterizer = text::Rasterizer::new(dpi_x, dpi_y, dpr);
let glyph_r = Glyph::new(&rasterizer.get_glyph(&desc, 180., 'R'));
let glyph_u = Glyph::new(&rasterizer.get_glyph(&desc, 180., 'u'));
let glyph_s = Glyph::new(&rasterizer.get_glyph(&desc, 180., 's'));
let glyph_t = Glyph::new(&rasterizer.get_glyph(&desc, 180., 't'));
let (cell_width, cell_height) = rasterizer.box_size_for_font(&desc, font_size);
let num_cols = grid::num_cells_axis(cell_width, sep_x, width);
let num_rows = grid::num_cells_axis(cell_height, sep_y, height);
println!("num_cols, num_rows = {}, {}", num_cols, num_rows);
let mut grid = Grid::new(num_rows as usize, num_cols as usize);
grid[0][0] = grid::Cell::new(Some(String::from("R")));
grid[0][1] = grid::Cell::new(Some(String::from("u")));
grid[0][2] = grid::Cell::new(Some(String::from("s")));
grid[0][3] = grid::Cell::new(Some(String::from("t")));
let mut glyph_cache = HashMap::new();
for c in INIT_LIST.chars() {
let glyph = Glyph::new(&rasterizer.get_glyph(&desc, font_size, c));
let string: String = c.escape_default().collect();
glyph_cache.insert(string, glyph);
}
unsafe {
gl::Enable(gl::BLEND);
@ -51,10 +80,22 @@ fn main() {
gl::Clear(gl::COLOR_BUFFER_BIT);
}
renderer.render(&glyph_r, 10.0, 10.0);
renderer.render(&glyph_u, 130.0, 10.0);
renderer.render(&glyph_s, 250.0, 10.0);
renderer.render(&glyph_t, 370.0, 10.0);
for i in 0..grid.rows() {
let row = &grid[i];
for j in 0..row.cols() {
let cell = &row[j];
if let Some(ref c) = cell.character {
if let Some(glyph) = glyph_cache.get(&c[..]) {
let y = (cell_height as f32 + sep_y as f32) * (i as f32);
let x = (cell_width as f32 + sep_x as f32) * (j as f32);
let y_inverted = (height as f32) - y - (cell_height as f32);
renderer.render(glyph, x, y_inverted);
}
}
}
}
window.swap_buffers().unwrap();

View File

@ -51,6 +51,21 @@ impl Rasterizer {
}
}
pub fn box_size_for_font(&mut self, desc: &FontDesc, size: f32) -> (u32, u32) {
let face = self.get_face(&desc).unwrap();
let scale_size = self.dpr * size;
let em_size = face.em_size() as f32;
let w = face.max_advance_width() as f32;
let h = face.height() as f32;
let w_scale = w / em_size;
let h_scale = h / em_size;
((w_scale * scale_size) as u32, (h_scale * scale_size) as u32)
}
pub fn get_face(&mut self, desc: &FontDesc) -> Option<Face<'static>> {
if let Some(face) = self.faces.get(desc) {
return Some(face.clone());
@ -71,7 +86,6 @@ impl Rasterizer {
pub fn get_glyph(&mut self, desc: &FontDesc, size: f32, c: char) -> RasterizedGlyph {
let face = self.get_face(desc).expect("TODO handle get_face error");
// TODO DPI
face.set_char_size(to_freetype_26_6(size * self.dpr), 0, self.dpi_x, self.dpi_y).unwrap();
face.load_char(c as usize, freetype::face::RENDER).unwrap();
let glyph = face.glyph();