blob: d7e46e8009b92225174c28e9e4969d4c170ba03d [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use carnelian::drawing::TextGrid;
use carnelian::render::{Context as RenderContext, Path};
use carnelian::Size;
use euclid::{point2, vec2};
use term_model::ansi::CursorStyle;
// Thickness of lines is determined by multiplying thickness factor
// with the cell height. 1/16 has been chosen as that results in 1px
// thick lines for a 16px cell height.
const LINE_THICKNESS_FACTOR: f32 = 1.0 / 16.0;
fn path_for_block(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
path_builder
.move_to(point2(0.0, 0.0))
.line_to(point2(size.width, 0.0))
.line_to(point2(size.width, size.height))
.line_to(point2(0.0, size.height))
.line_to(point2(0.0, 0.0));
path_builder.build()
}
pub struct Line {
position_y: f32,
thickness: f32,
}
impl Line {
pub fn new(line_metrics: ttf_parser::LineMetrics, textgrid: &TextGrid) -> Self {
let scale_y = textgrid.scale.y;
let offset_y = textgrid.offset.y;
let position = line_metrics.position as f32 * scale_y;
let thickness = line_metrics.thickness as f32 * scale_y;
let position_y = offset_y - position;
Self { thickness, position_y }
}
}
fn line_bounds(line: Option<Line>, default_bounds: (f32, f32), cell_height: f32) -> (f32, f32) {
line.map_or(default_bounds, |Line { thickness, position_y: line_y }| {
let top = line_y - thickness / 2.0;
let bottom = line_y + thickness / 2.0;
let (_, default_bottom) = default_bounds;
if bottom > cell_height {
return (default_bottom - thickness, default_bottom);
}
(top, bottom)
})
}
pub fn path_for_underline(
size: &Size,
render_context: &mut RenderContext,
line: Option<Line>,
) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let default_top = size.height - size.height * LINE_THICKNESS_FACTOR;
let default_bottom = size.height;
let (top, bottom) = line_bounds(line, (default_top, default_bottom), size.height);
path_builder
.move_to(point2(0.0, top))
.line_to(point2(size.width, top))
.line_to(point2(size.width, bottom))
.line_to(point2(0.0, bottom))
.line_to(point2(0.0, top));
path_builder.build()
}
fn path_for_beam(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let right = size.height * LINE_THICKNESS_FACTOR;
path_builder
.move_to(point2(0.0, 0.0))
.line_to(point2(right, 0.0))
.line_to(point2(right, size.height))
.line_to(point2(0.0, size.height))
.line_to(point2(0.0, 0.0));
path_builder.build()
}
fn path_for_hollow_block(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let inset = size.height * LINE_THICKNESS_FACTOR;
let bottom_start = size.height - inset;
let right_start = size.width - inset;
path_builder
// top
.move_to(point2(0.0, 0.0))
.line_to(point2(size.width, 0.0))
.line_to(point2(size.width, inset))
.line_to(point2(0.0, inset))
.line_to(point2(0.0, 0.0))
// bottom
.move_to(point2(0.0, bottom_start))
.line_to(point2(size.width, bottom_start))
.line_to(point2(size.width, size.height))
.line_to(point2(0.0, size.height))
.line_to(point2(0.0, bottom_start))
// left
.move_to(point2(0.0, inset))
.line_to(point2(inset, inset))
.line_to(point2(inset, bottom_start))
.line_to(point2(0.0, bottom_start))
.line_to(point2(0.0, inset))
// right
.move_to(point2(right_start, inset))
.line_to(point2(size.width, inset))
.line_to(point2(size.width, bottom_start))
.line_to(point2(right_start, bottom_start))
.line_to(point2(right_start, inset));
path_builder.build()
}
pub fn path_for_strikeout(
size: &Size,
render_context: &mut RenderContext,
line: Option<Line>,
) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let default_top = size.height / 2.0;
let default_bottom = default_top + size.height * LINE_THICKNESS_FACTOR;
let (top, bottom) = line_bounds(line, (default_top, default_bottom), size.height);
path_builder
.move_to(point2(0.0, top))
.line_to(point2(size.width, top))
.line_to(point2(size.width, bottom))
.line_to(point2(0.0, bottom))
.line_to(point2(0.0, top));
path_builder.build()
}
// Box Drawings Light Horizontal, "─".
fn path_for_unicode_2500(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let thickness = size.height * LINE_THICKNESS_FACTOR;
let top = size.height / 2.0 - thickness / 2.0;
let bottom = top + thickness;
path_builder
.move_to(point2(0.0, top))
.line_to(point2(size.width, top))
.line_to(point2(size.width, bottom))
.line_to(point2(0.0, bottom))
.line_to(point2(0.0, top));
path_builder.build()
}
// Box Drawings Light Vertical, "│".
fn path_for_unicode_2502(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let thickness = size.height * LINE_THICKNESS_FACTOR;
let left = size.width / 2.0 - thickness / 2.0;
let right = left + thickness;
path_builder
.move_to(point2(left, 0.0))
.line_to(point2(right, 0.0))
.line_to(point2(right, size.height))
.line_to(point2(left, size.height))
.line_to(point2(left, 0.0));
path_builder.build()
}
// Box Drawings Light Arc Down and Right, "╭".
fn path_for_unicode_256d(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let thickness = size.height * LINE_THICKNESS_FACTOR;
let bottom_left = size.width / 2.0 - thickness / 2.0;
let bottom_right = bottom_left + thickness;
let right_top = size.height / 2.0 - thickness / 2.0;
let right_bottom = right_top + thickness;
let radius = (size.height * 0.25).min(size.width * 0.25);
let inner_radius = radius - thickness / 2.0;
let outer_radius = inner_radius + thickness;
let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
let outer_control_dist = kappa * outer_radius;
let inner_control_dist = kappa * inner_radius;
let center = point2(size.width / 2.0, size.height / 2.0) + vec2(radius, radius);
let inner_p1 = center + vec2(-inner_control_dist, -inner_radius);
let inner_p2 = center + vec2(-inner_radius, -inner_control_dist);
let outer_p1 = center + vec2(-outer_radius, -outer_control_dist);
let outer_p2 = center + vec2(-outer_control_dist, -outer_radius);
path_builder
.move_to(point2(size.width, right_top))
.line_to(point2(size.width, right_bottom))
.line_to(point2(center.x, right_bottom))
.cubic_to(inner_p1, inner_p2, point2(bottom_right, center.y))
.line_to(point2(bottom_right, size.height))
.line_to(point2(bottom_left, size.height))
.line_to(point2(bottom_left, center.y))
.cubic_to(outer_p1, outer_p2, point2(center.x, right_top))
.line_to(point2(size.width, right_top));
path_builder.build()
}
// Box Drawings Light Arc Down and Left, "╮".
fn path_for_unicode_256e(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let thickness = size.height * LINE_THICKNESS_FACTOR;
let bottom_left = size.width / 2.0 - thickness / 2.0;
let bottom_right = bottom_left + thickness;
let left_top = size.height / 2.0 - thickness / 2.0;
let left_bottom = left_top + thickness;
let radius = (size.height * 0.25).min(size.width * 0.25);
let inner_radius = radius - thickness / 2.0;
let outer_radius = inner_radius + thickness;
let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
let outer_control_dist = kappa * outer_radius;
let inner_control_dist = kappa * inner_radius;
let center = point2(size.width / 2.0, size.height / 2.0) + vec2(-radius, radius);
let inner_p1 = center + vec2(inner_radius, -inner_control_dist);
let inner_p2 = center + vec2(inner_control_dist, -inner_radius);
let outer_p1 = center + vec2(outer_control_dist, -outer_radius);
let outer_p2 = center + vec2(outer_radius, -outer_control_dist);
path_builder
.move_to(point2(0.0, left_top))
.line_to(point2(center.x, left_top))
.cubic_to(outer_p1, outer_p2, point2(bottom_right, center.y))
.line_to(point2(bottom_right, size.height))
.line_to(point2(bottom_left, size.height))
.line_to(point2(bottom_left, center.y))
.cubic_to(inner_p1, inner_p2, point2(center.x, left_bottom))
.line_to(point2(0.0, left_bottom))
.line_to(point2(0.0, left_top));
path_builder.build()
}
// Box Drawings Light Arc Up and Left, "╯".
fn path_for_unicode_256f(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let thickness = size.height * LINE_THICKNESS_FACTOR;
let top_left = size.width / 2.0 - thickness / 2.0;
let top_right = top_left + thickness;
let left_top = size.height / 2.0 - thickness / 2.0;
let left_bottom = left_top + thickness;
let radius = (size.height * 0.25).min(size.width * 0.25);
let inner_radius = radius - thickness / 2.0;
let outer_radius = inner_radius + thickness;
let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
let outer_control_dist = kappa * outer_radius;
let inner_control_dist = kappa * inner_radius;
let center = point2(size.width / 2.0, size.height / 2.0) + vec2(-radius, -radius);
let inner_p1 = center + vec2(inner_control_dist, inner_radius);
let inner_p2 = center + vec2(inner_radius, inner_control_dist);
let outer_p1 = center + vec2(outer_radius, outer_control_dist);
let outer_p2 = center + vec2(outer_control_dist, outer_radius);
path_builder
.move_to(point2(top_left, 0.0))
.line_to(point2(top_right, 0.0))
.line_to(point2(top_right, center.y))
.cubic_to(outer_p1, outer_p2, point2(center.x, left_bottom))
.line_to(point2(0.0, left_bottom))
.line_to(point2(0.0, left_top))
.line_to(point2(center.x, left_top))
.cubic_to(inner_p1, inner_p2, point2(top_left, center.y))
.line_to(point2(top_left, 0.0));
path_builder.build()
}
// Box Drawings Light Arc Up and Right, "╰".
fn path_for_unicode_2570(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
let thickness = size.height * LINE_THICKNESS_FACTOR;
let top_left = size.width / 2.0 - thickness / 2.0;
let top_right = top_left + thickness;
let right_top = size.height / 2.0 - thickness / 2.0;
let right_bottom = right_top + thickness;
let radius = (size.height * 0.25).min(size.width * 0.25);
let inner_radius = radius - thickness / 2.0;
let outer_radius = inner_radius + thickness;
let kappa = 4.0 / 3.0 * (std::f32::consts::PI / 8.0).tan();
let outer_control_dist = kappa * outer_radius;
let inner_control_dist = kappa * inner_radius;
let center = point2(size.width / 2.0, size.height / 2.0) + vec2(radius, -radius);
let inner_p1 = center + vec2(-inner_radius, inner_control_dist);
let inner_p2 = center + vec2(-inner_control_dist, inner_radius);
let outer_p1 = center + vec2(-outer_control_dist, outer_radius);
let outer_p2 = center + vec2(-outer_radius, outer_control_dist);
path_builder
.move_to(point2(top_left, 0.0))
.line_to(point2(top_right, 0.0))
.line_to(point2(top_right, center.y))
.cubic_to(inner_p1, inner_p2, point2(center.x, right_top))
.line_to(point2(size.width, right_top))
.line_to(point2(size.width, right_bottom))
.line_to(point2(center.x, right_bottom))
.cubic_to(outer_p1, outer_p2, point2(top_left, center.y))
.line_to(point2(top_left, 0.0));
path_builder.build()
}
// Light Shade, "░".
fn path_for_unicode_2591(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
const GRID_SIZE: usize = 8;
let scale_x = size.width / GRID_SIZE as f32;
let scale_y = size.height / GRID_SIZE as f32;
for y in 0..GRID_SIZE {
for x in 0..GRID_SIZE {
let offset = if y % 2 == 0 { x } else { x + 2 };
// Fill every fourth grid cell.
if offset % 4 == 0 {
let x0 = x as f32 * scale_x;
let y0 = y as f32 * scale_y;
let x1 = (x + 1) as f32 * scale_x;
let y1 = (y + 1) as f32 * scale_y;
path_builder
.move_to(point2(x0, y0))
.line_to(point2(x1, y0))
.line_to(point2(x1, y1))
.line_to(point2(x0, y1))
.line_to(point2(x0, y0));
}
}
}
path_builder.build()
}
// Medium Shade, "▒".
fn path_for_unicode_2592(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
const GRID_SIZE: usize = 9;
let scale_x = size.width / GRID_SIZE as f32;
let scale_y = size.height / GRID_SIZE as f32;
for y in 0..GRID_SIZE {
for x in 0..GRID_SIZE {
let offset = if y % 2 == 0 { x } else { x + 1 };
// Fill every other grid cell.
if offset % 2 == 0 {
let x0 = x as f32 * scale_x;
let y0 = y as f32 * scale_y;
let x1 = (x + 1) as f32 * scale_x;
let y1 = (y + 1) as f32 * scale_y;
path_builder
.move_to(point2(x0, y0))
.line_to(point2(x1, y0))
.line_to(point2(x1, y1))
.line_to(point2(x0, y1))
.line_to(point2(x0, y0));
}
}
}
path_builder.build()
}
// Dark Shade, "▓".
fn path_for_unicode_2593(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
const GRID_SIZE: usize = 8;
let scale_x = size.width / GRID_SIZE as f32;
let scale_y = size.height / GRID_SIZE as f32;
for y in 0..GRID_SIZE {
for x in 0..GRID_SIZE {
let offset = if y % 2 == 0 { x } else { x + 2 };
// Skip every fourth grid cell.
if offset % 4 != 0 {
let x0 = x as f32 * scale_x;
let y0 = y as f32 * scale_y;
let x1 = (x + 1) as f32 * scale_x;
let y1 = (y + 1) as f32 * scale_y;
path_builder
.move_to(point2(x0, y0))
.line_to(point2(x1, y0))
.line_to(point2(x1, y1))
.line_to(point2(x0, y1))
.line_to(point2(x0, y0));
}
}
}
path_builder.build()
}
// Heavy Check Mark, "✔".
fn path_for_unicode_2714(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
const OFFSET_FACTOR: f32 = 1.0 / 12.0;
let offset = size.height * OFFSET_FACTOR;
let center_offset = offset * 3.0;
let left_top = 0.4 * size.height;
let right_top = 0.25 * size.height;
let center = 0.3 * size.width;
let bottom = 0.75 * size.height;
let left = 0.05;
let right = size.width - 0.05;
path_builder
.move_to(point2(left, left_top + offset))
.line_to(point2(left + offset, left_top))
.line_to(point2(center, bottom - center_offset))
.line_to(point2(right - offset, right_top))
.line_to(point2(right, right_top + offset))
.line_to(point2(center, bottom))
.line_to(point2(left, left_top + offset));
path_builder.build()
}
// Heavy Ballot X, "✘".
fn path_for_unicode_2718(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
const OFFSET_FACTOR: f32 = 1.0 / 12.0;
let offset = size.height * OFFSET_FACTOR;
let center_offset = offset;
let top = 0.25 * size.height;
let center_x = size.width / 2.0;
let center_y = size.height / 2.0;
let bottom = 0.75 * size.height;
let left = 0.05;
let right = size.width - 0.05;
path_builder
.move_to(point2(left, top + offset))
.line_to(point2(left + offset, top))
.line_to(point2(center_x, center_y - center_offset))
.line_to(point2(right - offset, top))
.line_to(point2(right, top + offset))
.line_to(point2(center_x + center_offset, center_y))
.line_to(point2(right, bottom - offset))
.line_to(point2(right - offset, bottom))
.line_to(point2(center_x, center_y + center_offset))
.line_to(point2(left + offset, bottom))
.line_to(point2(left, bottom - offset))
.line_to(point2(center_x - center_offset, center_y))
.line_to(point2(left, top + offset));
path_builder.build()
}
// Heavy Left-Pointing Angle Quotation Mark Ornament, "❮".
fn path_for_unicode_276e(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
const THICKNESS_FACTOR: f32 = 1.0 / 8.0;
let thickness = size.height * THICKNESS_FACTOR;
let top = 0.25 * size.height;
let bottom = 0.85 * size.height;
let left = 0.2 * size.width;
let right = 0.8 * size.width;
let center_y = top + (bottom - top) / 2.0;
path_builder
.move_to(point2(right - thickness, top))
.line_to(point2(right, top))
.line_to(point2(left + thickness, center_y))
.line_to(point2(right, bottom))
.line_to(point2(right - thickness, bottom))
.line_to(point2(left, center_y))
.line_to(point2(right - thickness, top));
path_builder.build()
}
// Heavy Right-Pointing Angle Quotation Mark Ornament, "❯".
fn path_for_unicode_276f(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
const THICKNESS_FACTOR: f32 = 1.0 / 8.0;
let thickness = size.height * THICKNESS_FACTOR;
let top = 0.25 * size.height;
let bottom = 0.85 * size.height;
let left = 0.2 * size.width;
let right = 0.8 * size.width;
let center_y = top + (bottom - top) / 2.0;
path_builder
.move_to(point2(left, top))
.line_to(point2(left + thickness, top))
.line_to(point2(right, center_y))
.line_to(point2(left + thickness, bottom))
.line_to(point2(left, bottom))
.line_to(point2(right - thickness, center_y))
.line_to(point2(left, top));
path_builder.build()
}
// Triangle right, "".
fn path_for_unicode_e0b0(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
path_builder
.move_to(point2(0.0, 0.0))
.line_to(point2(size.width, size.height / 2.0))
.line_to(point2(0.0, size.height))
.line_to(point2(0.0, 0.0));
path_builder.build()
}
// Angle right, "".
fn path_for_unicode_e0b1(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
// Requires 45 degree angle for correct thickness.
let thickness = size.height * LINE_THICKNESS_FACTOR;
let offset = (2.0 * thickness * thickness).sqrt() / 2.0;
path_builder
.move_to(point2(offset, 0.0))
.line_to(point2(size.width + offset, size.height / 2.0))
.line_to(point2(offset, size.height))
.line_to(point2(-offset, size.height))
.line_to(point2(size.width - offset, size.height / 2.0))
.line_to(point2(-offset, 0.0))
.line_to(point2(offset, 0.0));
path_builder.build()
}
// Triangle left, "".
fn path_for_unicode_e0b2(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
path_builder
.move_to(point2(size.width, 0.0))
.line_to(point2(size.width, size.height))
.line_to(point2(0.0, size.height / 2.0))
.line_to(point2(size.width, 0.0));
path_builder.build()
}
// Angle left, "".
fn path_for_unicode_e0b3(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
// Requires 45 degree angle for correct thickness.
let thickness = size.height * LINE_THICKNESS_FACTOR;
let offset = (2.0 * thickness * thickness).sqrt() / 2.0;
path_builder
.move_to(point2(size.width + offset, 0.0))
.line_to(point2(offset, size.height / 2.0))
.line_to(point2(size.width + offset, size.height))
.line_to(point2(size.width - offset, size.height))
.line_to(point2(-offset, size.height / 2.0))
.line_to(point2(size.width - offset, 0.0))
.line_to(point2(size.width + offset, 0.0));
path_builder.build()
}
// Lower right triangle, "".
fn path_for_unicode_e0ba(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
path_builder
.move_to(point2(size.width, 0.0))
.line_to(point2(size.width, size.height))
.line_to(point2(0.0, size.height))
.line_to(point2(size.width, 0.0));
path_builder.build()
}
// Upper left triangle, "".
fn path_for_unicode_e0bc(size: &Size, render_context: &mut RenderContext) -> Path {
let mut path_builder = render_context.path_builder().expect("path_builder");
path_builder
.move_to(point2(0.0, 0.0))
.line_to(point2(size.width, 0.0))
.line_to(point2(0.0, size.height))
.line_to(point2(0.0, 0.0));
path_builder.build()
}
pub fn maybe_path_for_cursor_style(
render_context: &mut RenderContext,
cursor_style: CursorStyle,
cell_size: &Size,
) -> Option<Path> {
match cursor_style {
CursorStyle::Block => Some(path_for_block(cell_size, render_context)),
CursorStyle::Underline => Some(path_for_underline(cell_size, render_context, None)),
CursorStyle::Beam => Some(path_for_beam(cell_size, render_context)),
CursorStyle::HollowBlock => Some(path_for_hollow_block(cell_size, render_context)),
CursorStyle::Hidden => None,
}
}
pub fn maybe_path_for_char(
render_context: &mut RenderContext,
c: char,
cell_size: &Size,
) -> Option<Path> {
match c {
'\u{2500}' => Some(path_for_unicode_2500(cell_size, render_context)),
'\u{2502}' => Some(path_for_unicode_2502(cell_size, render_context)),
'\u{256d}' => Some(path_for_unicode_256d(cell_size, render_context)),
'\u{256e}' => Some(path_for_unicode_256e(cell_size, render_context)),
'\u{256f}' => Some(path_for_unicode_256f(cell_size, render_context)),
'\u{2570}' => Some(path_for_unicode_2570(cell_size, render_context)),
'\u{2591}' => Some(path_for_unicode_2591(cell_size, render_context)),
'\u{2592}' => Some(path_for_unicode_2592(cell_size, render_context)),
'\u{2593}' => Some(path_for_unicode_2593(cell_size, render_context)),
'\u{2714}' => Some(path_for_unicode_2714(cell_size, render_context)),
'\u{2718}' => Some(path_for_unicode_2718(cell_size, render_context)),
'\u{276e}' => Some(path_for_unicode_276e(cell_size, render_context)),
'\u{276f}' => Some(path_for_unicode_276f(cell_size, render_context)),
'\u{e0b0}' => Some(path_for_unicode_e0b0(cell_size, render_context)),
'\u{e0b1}' => Some(path_for_unicode_e0b1(cell_size, render_context)),
'\u{e0b2}' => Some(path_for_unicode_e0b2(cell_size, render_context)),
'\u{e0b3}' => Some(path_for_unicode_e0b3(cell_size, render_context)),
'\u{e0ba}' => Some(path_for_unicode_e0ba(cell_size, render_context)),
'\u{e0bc}' => Some(path_for_unicode_e0bc(cell_size, render_context)),
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::Error;
use carnelian::drawing::{DisplayRotation, FontFace};
use carnelian::render::{generic, ContextInner};
use euclid::size2;
use once_cell::sync::Lazy;
// This font creation method isn't ideal. The correct method would be to ask the Fuchsia
// font service for the font data.
static FONT_DATA: &'static [u8] = include_bytes!(
"../../../../../prebuilt/third_party/fonts/robotomono/RobotoMono-Regular.ttf"
);
static FONT_FACE: Lazy<FontFace> =
Lazy::new(|| FontFace::new(&FONT_DATA).expect("Failed to create font"));
#[test]
fn check_cursor_paths() -> Result<(), Error> {
const SUPPORTED_CURSOR_STYLES: &[CursorStyle] = &[
CursorStyle::Block,
CursorStyle::Underline,
CursorStyle::Beam,
CursorStyle::HollowBlock,
];
let size = size2(64, 64);
let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
let cell_size = Size::new(8.0, 16.0);
for cursor_style in SUPPORTED_CURSOR_STYLES {
let result =
maybe_path_for_cursor_style(&mut render_context, *cursor_style, &cell_size);
assert_eq!(result.is_some(), true);
}
Ok(())
}
#[test]
fn check_strikeout_path() -> Result<(), Error> {
let size = size2(64, 64);
let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
let cell_size = Size::new(8.0, 16.0);
let textgrid = TextGrid::new(&FONT_FACE, &cell_size);
let _ = path_for_strikeout(
&cell_size,
&mut render_context,
FONT_FACE
.face
.strikeout_metrics()
.map(|line_metrics| Line::new(line_metrics, &textgrid)),
);
Ok(())
}
#[test]
fn check_underline_path() -> Result<(), Error> {
let size = size2(64, 64);
let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
let cell_size = Size::new(8.0, 16.0);
let textgrid = TextGrid::new(&FONT_FACE, &cell_size);
let _ = path_for_underline(
&cell_size,
&mut render_context,
FONT_FACE
.face
.underline_metrics()
.map(|line_metrics| Line::new(line_metrics, &textgrid)),
);
Ok(())
}
#[test]
fn check_unicode_paths() -> Result<(), Error> {
const SUPPORTED_UNICODE_CHARS: &[char] = &[
'\u{2500}', '\u{2502}', '\u{256d}', '\u{256e}', '\u{256f}', '\u{2570}', '\u{2591}',
'\u{2592}', '\u{2593}', '\u{2714}', '\u{2718}', '\u{276e}', '\u{276f}', '\u{e0b0}',
'\u{e0b1}', '\u{e0b2}', '\u{e0b3}', '\u{e0ba}', '\u{e0bc}',
];
let size = size2(64, 64);
let forma_context = generic::Forma::new_context_without_token(size, DisplayRotation::Deg0);
let mut render_context = RenderContext { inner: ContextInner::Forma(forma_context) };
let cell_size = Size::new(8.0, 16.0);
for c in SUPPORTED_UNICODE_CHARS {
let result = maybe_path_for_char(&mut render_context, *c, &cell_size);
assert_eq!(result.is_some(), true);
}
Ok(())
}
}