extract a font layer and reimplement the bricks font (#23)

* implement the font layer

* run fmt

* refactor
This commit is contained in:
Jimmy 2022-09-05 21:01:43 +08:00 committed by GitHub
parent 8f434e2d05
commit 25d6795d7e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 171 additions and 283 deletions

View file

@ -6,9 +6,9 @@ mod timer;
use std::cmp::min;
use std::fmt::Write as _;
use crate::clock_text::BricksText;
use chrono::Duration;
pub(crate) use clock::Clock;
use crate::bricks_text::BricksText;
pub(crate) use countdown::Countdown;
pub(crate) use stopwatch::Stopwatch;
pub(crate) use timer::Timer;

View file

@ -1,6 +1,6 @@
use crate::clock_text::BricksText;
use chrono::{Local, Utc};
use chrono_tz::Tz;
use crate::bricks_text::BricksText;
use tui::{layout::Rect, style::Style, widgets::Widget};
use super::render_centered;

View file

@ -1,5 +1,5 @@
use crate::clock_text::BricksText;
use chrono::{DateTime, Duration, Local};
use crate::bricks_text::BricksText;
use tui::{style::Style, widgets::Widget};
use super::{format_duration, render_centered, DurationFormat};

View file

@ -1,5 +1,5 @@
use crate::clock_text::BricksText;
use chrono::{DateTime, Duration, Local};
use crate::bricks_text::BricksText;
use tui::{buffer::Buffer, layout::Rect, style::Style, widgets::Widget};
use crate::app::Pause;

View file

@ -1,7 +1,7 @@
use std::{cell::RefCell, process::Command};
use crate::clock_text::BricksText;
use chrono::{DateTime, Duration, Local};
use crate::bricks_text::BricksText;
use tui::{buffer::Buffer, layout::Rect, style::Style, widgets::Widget};
use crate::app::Pause;

View file

@ -4,8 +4,8 @@ use std::{
time::{Duration, Instant},
};
use clock_tui::app::Mode;
use clap::Parser;
use clock_tui::app::Mode;
use crossterm::{
event::{self, Event, KeyCode},
execute,

View file

@ -1,265 +0,0 @@
use std::{cmp::max, ops};
use tui::{buffer::Buffer, layout::Rect, style::Style};
pub(crate) trait BricksChar {
fn render(&self, size: u16, style: Style, area: Rect, buf: &mut Buffer);
}
pub struct BrickChar(char);
impl BrickChar {
const H_UNIT: u16 = 2;
const V_UNIT: u16 = 1;
const UNIT_SIZE: Point = Point(3 * Self::H_UNIT, 5 * Self::V_UNIT);
pub(crate) fn size(size: u16) -> Point {
Self::UNIT_SIZE * size
}
pub(crate) fn from(char: char) -> BrickChar {
BrickChar(char)
}
pub(crate) fn render(&self, size: u16, style: Style, area: Rect, buf: &mut Buffer) {
let char_size = BrickChar::size(size);
match self.0 {
'0'..='9' => Self::draw_digital(self.0, size, style, area, buf),
':' => {
let start_x = area.x + size * Self::H_UNIT;
let end_x = area.x + char_size.0 - size * Self::H_UNIT;
let start_y = area.y + size * Self::V_UNIT;
let start_y2 = area.y + (char_size.1 + size * Self::V_UNIT) / 2;
let len = (char_size.1 - 3 * size * Self::V_UNIT) / 2;
for x in (start_x..end_x).step_by((size * Self::H_UNIT) as usize) {
Self::draw_line(
size,
Point(x, start_y),
len,
LineDir::Vertical,
style,
&area,
buf,
);
Self::draw_line(
size,
Point(x, start_y2),
len,
LineDir::Vertical,
style,
&area,
buf,
);
}
}
'-' => {
let x = area.x;
let y = area.y + (char_size.1 - size * Self::V_UNIT) / 2;
Self::draw_line(
size,
Point(x, y),
char_size.1,
LineDir::Horizontal,
style,
&area,
buf,
);
}
'.' => {
let x = area.x + char_size.0 - size * Self::H_UNIT;
let y = area.y + char_size.1 - size * Self::V_UNIT;
Self::draw_line(
size,
Point(x, y),
size * Self::H_UNIT,
LineDir::Horizontal,
style,
&area,
buf,
);
}
_ => {}
}
}
fn draw_digital(d: char, size: u16, style: Style, area: Rect, buf: &mut Buffer) {
let char_size = BrickChar::size(size);
let mut draw_line =
|x, y, len, dir| Self::draw_line(size, Point(x, y), len, dir, style, &area, buf);
let x_start = area.x;
let x_end = area.x + char_size.0 - size * Self::H_UNIT;
let y_start = area.y;
let y_end = area.y + char_size.1 - size * Self::V_UNIT;
let y_center = area.y + (char_size.1 - size * Self::V_UNIT) / 2;
let half_h = (char_size.1 + size * Self::V_UNIT) / 2;
match d {
'0' => {
draw_line(x_start, y_start, half_h, LineDir::Vertical);
draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
// draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'1' => {
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
// draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
// draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
// draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'2' => {
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
// draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'3' => {
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'4' => {
draw_line(x_start, y_start, half_h, LineDir::Vertical);
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
// draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
// draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'5' => {
draw_line(x_start, y_start, half_h, LineDir::Vertical);
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
// draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'6' => {
draw_line(x_start, y_start, half_h, LineDir::Vertical);
draw_line(x_start, y_center, half_h, LineDir::Vertical);
// draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'7' => {
// draw_line(x_start, y_start, half_h, LineDir::Vertical);
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
// draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
// draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'8' => {
draw_line(x_start, y_start, half_h, LineDir::Vertical);
draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
'9' => {
draw_line(x_start, y_start, half_h, LineDir::Vertical);
// draw_line(x_start, y_center, half_h, LineDir::Vertical);
draw_line(x_end, y_start, half_h, LineDir::Vertical);
draw_line(x_end, y_center, half_h, LineDir::Vertical);
draw_line(x_start, y_start, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_center, char_size.0, LineDir::Horizontal);
draw_line(x_start, y_end, char_size.0, LineDir::Horizontal);
}
_ => {}
}
}
fn draw_line(
size: u16,
start: Point,
len: u16,
dir: LineDir,
style: Style,
area: &Rect,
buf: &mut Buffer,
) {
let step = match dir {
LineDir::Horizontal => Point(Self::H_UNIT, 0),
LineDir::Vertical => Point(0, Self::V_UNIT),
};
let line = match dir {
LineDir::Horizontal => Point(0, Self::V_UNIT),
LineDir::Vertical => Point(Self::H_UNIT, 0),
};
let mut from = start;
for _ in 0..size {
let mut p = from;
for _ in (0..len).step_by(max(step.0, step.1).into()) {
if !p.in_area(area) {
break;
}
// println!("p = {:?} area = {:?}", p, area);
buf.get_mut(p.0, p.1).set_symbol("██").set_style(style);
p = p + &step;
}
from = from + &line;
}
}
}
enum LineDir {
Horizontal,
Vertical,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Point(pub u16, pub u16);
impl Point {
pub(crate) fn in_area(&self, area: &Rect) -> bool {
area.left() <= self.0
&& self.0 < area.right()
&& area.top() <= self.1
&& self.1 < area.bottom()
}
}
impl ops::Add<&Point> for Point {
type Output = Point;
fn add(self, other: &Point) -> Point {
Point(self.0 + other.0, self.1 + other.1)
}
}
impl ops::Mul<u16> for Point {
type Output = Point;
fn mul(self, other: u16) -> Point {
Point(self.0 * other, self.1 * other)
}
}

View file

@ -1,28 +1,31 @@
use tui::{style::Style, widgets::Widget};
use self::chars::{BrickChar, Point};
use self::font::bricks::Bricks;
use self::font::Font;
use self::point::Point;
mod chars;
mod font;
mod point;
pub struct BricksText {
text: String,
size: u16,
space: u16,
style: Style,
font: Bricks,
}
impl BricksText {
pub fn new(text: &str, size: u16, space: u16, style: Style) -> BricksText {
BricksText {
text: text.to_string(),
size,
space,
style,
font: Bricks { size },
}
}
pub fn size(&self) -> (u16, u16) {
let Point(w, h) = BrickChar::size(self.size);
let Point(w, h) = self.font.size();
let n_chars = self.text.chars().count() as u16;
(w * n_chars + self.space * (n_chars - 1), h)
}
@ -32,9 +35,8 @@ impl Widget for &BricksText {
fn render(self, area: tui::layout::Rect, buf: &mut tui::buffer::Buffer) {
let mut area = area;
for char in self.text.chars() {
let Point(w, _) = BrickChar::size(self.size);
let char = BrickChar::from(char);
char.render(self.size, self.style, area, buf);
let Point(w, _) = self.font.size();
self.font.render(char, self.style, area, buf);
let l = w + self.space;
area.x += l;
area.width = area.width.saturating_sub(l);

View file

@ -0,0 +1,10 @@
use tui::{buffer::Buffer, layout::Rect, style::Style};
use super::point::Point;
pub mod bricks;
pub(crate) trait Font {
fn size(&self) -> Point;
fn render(&self, char: char, style: Style, area: Rect, buf: &mut Buffer);
}

View file

@ -0,0 +1,121 @@
use std::cmp::min;
use tui::{buffer::Buffer, layout::Rect, style::Style};
use super::Font;
use crate::clock_text::point::Point;
pub struct Bricks {
pub size: u16,
}
impl Bricks {
const UNIT_SIZE: Point = Point(6, 5);
/// each row is represented with a vector of numbers:
/// the odd indexed items represent the lenght of "off",
/// the even indexed items represent the lenght of "on".
/// For exmaple:
/// vec![0, 6] is "██████"
/// vec![2, 2] is " ██"
/// vec![0, 2, 2, 2] is "██ ██"
fn draw_row(
start: Point,
row: Vec<u16>,
size: u16,
style: Style,
area: &Rect,
buf: &mut Buffer,
) {
let mut p = start;
let mut on = false;
for len in row {
let len = len * size;
if p.0 > area.right() {
break;
}
if on {
let s = min(len, area.right() - p.0 + 1);
let line = "".repeat(s as usize);
for r in 0..size {
if p.1 > area.bottom() {
break;
}
buf.set_string(p.0, p.1 + r, line.as_str(), style);
}
}
p.0 += len;
on = !on;
}
}
fn draw_matrix(mat: [Vec<u16>; 5], size: u16, style: Style, area: &Rect, buf: &mut Buffer) {
let mut start = Point(area.x, area.y);
for row in mat {
Self::draw_row(start, row, size, style, area, buf);
start.1 += size;
}
}
}
impl Font for Bricks {
fn size(&self) -> Point {
Self::UNIT_SIZE * self.size
}
fn render(&self, char: char, style: Style, area: Rect, buf: &mut Buffer) {
let size = self.size;
let mut render_matrix = |mat: [Vec<u16>; 5]| {
Bricks::draw_matrix(mat, size, style, &area, buf);
};
match char {
'0' => render_matrix([
vec![0, 6],
vec![0, 2, 2, 2],
vec![0, 2, 2, 2],
vec![0, 2, 2, 2],
vec![0, 6],
]),
'1' => render_matrix([vec![0, 4], vec![2, 2], vec![2, 2], vec![2, 2], vec![0, 6]]),
'2' => render_matrix([vec![0, 6], vec![4, 2], vec![0, 6], vec![0, 2], vec![0, 6]]),
'3' => render_matrix([vec![0, 6], vec![4, 2], vec![0, 6], vec![4, 2], vec![0, 6]]),
'4' => render_matrix([
vec![0, 2, 2, 2],
vec![0, 2, 2, 2],
vec![0, 6],
vec![4, 2],
vec![4, 2],
]),
'5' => render_matrix([vec![0, 6], vec![0, 2], vec![0, 6], vec![4, 2], vec![0, 6]]),
'6' => render_matrix([
vec![0, 6],
vec![0, 2],
vec![0, 6],
vec![0, 2, 2, 2],
vec![0, 6],
]),
'7' => render_matrix([vec![0, 6], vec![4, 2], vec![4, 2], vec![4, 2], vec![4, 2]]),
'8' => render_matrix([
vec![0, 6],
vec![0, 2, 2, 2],
vec![0, 6],
vec![0, 2, 2, 2],
vec![0, 6],
]),
'9' => render_matrix([
vec![0, 6],
vec![0, 2, 2, 2],
vec![0, 6],
vec![4, 2],
vec![0, 6],
]),
':' => render_matrix([vec![], vec![2, 2], vec![], vec![2, 2], vec![]]),
'.' => render_matrix([vec![], vec![], vec![], vec![], vec![2, 2]]),
'-' => render_matrix([vec![], vec![], vec![0, 6], vec![], vec![]]),
_ => {}
}
}
}

View file

@ -0,0 +1,20 @@
use std::ops;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Point(pub u16, pub u16);
impl ops::Add<&Point> for Point {
type Output = Point;
fn add(self, other: &Point) -> Point {
Point(self.0 + other.0, self.1 + other.1)
}
}
impl ops::Mul<u16> for Point {
type Output = Point;
fn mul(self, other: u16) -> Point {
Point(self.0 * other, self.1 * other)
}
}

View file

@ -1,2 +1,2 @@
pub mod app;
pub mod bricks_text;
pub mod clock_text;

View file

@ -1,10 +1,10 @@
use clap::{IntoApp, ArgEnum};
use clap_complete::{Shell, generate_to};
use clap::{ArgEnum, IntoApp};
use clap_complete::{generate_to, Shell};
use clap_mangen::Man;
use clock_tui::app::App;
use std::fs::File;
use std::io::Result;
use std::path::{PathBuf, Path};
use std::path::{Path, PathBuf};
use std::{env, fs};
const BIN_NAME: &str = "tclock";