use crate::base::{Point, Rect, Rgba, Size};
pub trait StyledCanvas: Canvas {
fn print_styled(&mut self, p: Point, text: &str, style: &crate::render::Style) -> i32 {
self.print(
p,
text,
style.fg.unwrap_or(Rgba::WHITE),
style.bg.unwrap_or(Rgba::TRANSPARENT),
)
}
fn register_link(&mut self, uri: &str) -> u16 {
let _ = uri;
0
}
fn fill_styled(&mut self, rect: Rect, ch: char, style: &crate::render::Style) {
let mut buf = [0u8; 4];
let s: &str = ch.encode_utf8(&mut buf);
for y in rect.y..rect.bottom() {
for x in rect.x..rect.right() {
self.print_styled(Point::new(x, y), s, style);
}
}
}
}
pub trait Canvas {
fn size(&self) -> Size;
fn clip_rect(&self) -> Rect {
Rect::from_size(self.size())
}
fn put(&mut self, p: Point, ch: char, fg: Rgba, bg: Rgba);
fn fill(&mut self, rect: Rect, ch: char, fg: Rgba, bg: Rgba) {
for y in rect.y..rect.bottom() {
for x in rect.x..rect.right() {
self.put(Point::new(x, y), ch, fg, bg);
}
}
}
fn print(&mut self, p: Point, text: &str, fg: Rgba, bg: Rgba) -> i32 {
let mut x = p.x;
for ch in text.chars() {
self.put(Point::new(x, p.y), ch, fg, bg);
x += 1;
}
x - p.x
}
}
pub struct BufferCanvas {
size: Size,
cells: Vec<(char, Rgba, Rgba)>,
attrs: Vec<crate::render::Attrs>,
}
impl BufferCanvas {
pub fn new(size: Size) -> Self {
let n = (size.w.max(0) * size.h.max(0)) as usize;
BufferCanvas {
size,
cells: vec![(' ', Rgba::WHITE, Rgba::TRANSPARENT); n],
attrs: vec![crate::render::Attrs::NONE; n],
}
}
pub fn cell(&self, p: Point) -> Option<(char, Rgba, Rgba)> {
if p.x < 0 || p.y < 0 || p.x >= self.size.w || p.y >= self.size.h {
return None;
}
Some(self.cells[(p.y * self.size.w + p.x) as usize])
}
pub fn attrs_at(&self, p: Point) -> crate::render::Attrs {
if p.x < 0 || p.y < 0 || p.x >= self.size.w || p.y >= self.size.h {
return crate::render::Attrs::NONE;
}
self.attrs[(p.y * self.size.w + p.x) as usize]
}
pub fn row_text(&self, y: i32) -> String {
(0..self.size.w)
.map(|x| self.cell(Point::new(x, y)).map(|c| c.0).unwrap_or(' '))
.collect()
}
}
impl Canvas for BufferCanvas {
fn size(&self) -> Size {
self.size
}
fn put(&mut self, p: Point, ch: char, fg: Rgba, bg: Rgba) {
if p.x < 0 || p.y < 0 || p.x >= self.size.w || p.y >= self.size.h {
return; }
let idx = (p.y * self.size.w + p.x) as usize;
let prev = self.cells[idx];
let bg = if bg.is_transparent() { prev.2 } else { bg };
self.cells[idx] = (ch, fg, bg);
self.attrs[idx] = crate::render::Attrs::NONE; }
}
impl StyledCanvas for BufferCanvas {
fn print_styled(&mut self, p: Point, text: &str, style: &crate::render::Style) -> i32 {
let advanced = self.print(
p,
text,
style.fg.unwrap_or(Rgba::WHITE),
style.bg.unwrap_or(Rgba::TRANSPARENT),
);
let attrs = crate::render::Attrs::NONE
.without(style.remove)
.with(style.add);
for x in p.x..p.x + advanced {
if x >= 0 && x < self.size.w && p.y >= 0 && p.y < self.size.h {
self.attrs[(p.y * self.size.w + x) as usize] = attrs;
}
}
advanced
}
}
pub struct SurfaceCanvas<'a> {
surface: &'a mut crate::render::Surface,
}
impl<'a> SurfaceCanvas<'a> {
pub fn new(surface: &'a mut crate::render::Surface) -> Self {
SurfaceCanvas { surface }
}
fn style_for(fg: Rgba, bg: Rgba) -> crate::render::Style {
let mut style = crate::render::Style::new();
if !fg.is_transparent() {
style = style.fg(fg);
}
if !bg.is_transparent() {
style = style.bg(bg);
}
style
}
}
impl Canvas for SurfaceCanvas<'_> {
fn size(&self) -> Size {
self.surface.size()
}
fn put(&mut self, p: Point, ch: char, fg: Rgba, bg: Rgba) {
let mut buf = [0u8; 4];
let s: &str = ch.encode_utf8(&mut buf);
self.surface.draw_text(p.x, p.y, s, Self::style_for(fg, bg));
}
fn fill(&mut self, rect: Rect, ch: char, fg: Rgba, bg: Rgba) {
if ch == ' ' {
let cell = crate::render::Cell::new(crate::render::Glyph::SPACE)
.with_fg(fg)
.with_bg(bg);
self.surface.fill_rect(rect, cell);
return;
}
for y in rect.y..rect.bottom() {
for x in rect.x..rect.right() {
self.put(Point::new(x, y), ch, fg, bg);
}
}
}
fn print(&mut self, p: Point, text: &str, fg: Rgba, bg: Rgba) -> i32 {
self.surface
.draw_text(p.x, p.y, text, Self::style_for(fg, bg))
}
}
impl StyledCanvas for SurfaceCanvas<'_> {
fn register_link(&mut self, uri: &str) -> u16 {
self.surface.register_link(uri)
}
fn print_styled(&mut self, p: Point, text: &str, style: &crate::render::Style) -> i32 {
self.surface.draw_text(p.x, p.y, text, *style)
}
fn fill_styled(&mut self, rect: Rect, ch: char, style: &crate::render::Style) {
if ch == ' ' {
let cell = style.apply(&crate::render::Cell::new(crate::render::Glyph::SPACE));
self.surface.fill_rect(rect, cell);
return;
}
let mut buf = [0u8; 4];
let s: &str = ch.encode_utf8(&mut buf);
for y in rect.y..rect.bottom() {
for x in rect.x..rect.right() {
self.surface.draw_text(x, y, s, *style);
}
}
}
}
pub struct ClippedCanvas<'a> {
inner: &'a mut dyn StyledCanvas,
clip: Rect,
}
impl<'a> ClippedCanvas<'a> {
pub fn new(inner: &'a mut dyn StyledCanvas, clip: Rect) -> Self {
ClippedCanvas { inner, clip }
}
}
impl Canvas for ClippedCanvas<'_> {
fn size(&self) -> Size {
self.inner.size()
}
fn clip_rect(&self) -> Rect {
self.clip
}
fn put(&mut self, p: Point, ch: char, fg: Rgba, bg: Rgba) {
if self.clip.contains(p) {
self.inner.put(p, ch, fg, bg);
}
}
fn fill(&mut self, rect: Rect, ch: char, fg: Rgba, bg: Rgba) {
let clipped = rect.intersect(self.clip);
if !clipped.is_empty() {
self.inner.fill(clipped, ch, fg, bg);
}
}
fn print(&mut self, p: Point, text: &str, fg: Rgba, bg: Rgba) -> i32 {
if p.y < self.clip.y || p.y >= self.clip.bottom() {
return 0;
}
let total = crate::text::width(text);
if p.x >= self.clip.x && p.x + total <= self.clip.right() {
return self.inner.print(p, text, fg, bg);
}
let mut x = p.x;
let mut buf = [0u8; 4];
for ch in text.chars() {
let s: &str = ch.encode_utf8(&mut buf);
let w = crate::text::cluster_width(s);
if w <= 0 {
continue;
}
if x >= self.clip.x && x + w <= self.clip.right() {
self.inner.print(Point::new(x, p.y), s, fg, bg);
}
x += w;
}
x - p.x
}
}
impl StyledCanvas for ClippedCanvas<'_> {
fn register_link(&mut self, uri: &str) -> u16 {
self.inner.register_link(uri)
}
fn print_styled(&mut self, p: Point, text: &str, style: &crate::render::Style) -> i32 {
if p.y < self.clip.y || p.y >= self.clip.bottom() {
return 0;
}
let total = crate::text::width(text);
if p.x >= self.clip.x && p.x + total <= self.clip.right() {
return self.inner.print_styled(p, text, style);
}
let mut x = p.x;
let mut buf = [0u8; 4];
for ch in text.chars() {
let s: &str = ch.encode_utf8(&mut buf);
let w = crate::text::cluster_width(s);
if w <= 0 {
continue;
}
if x >= self.clip.x && x + w <= self.clip.right() {
self.inner.print_styled(Point::new(x, p.y), s, style);
}
x += w;
}
x - p.x
}
fn fill_styled(&mut self, rect: Rect, ch: char, style: &crate::render::Style) {
let clipped = rect.intersect(self.clip);
if !clipped.is_empty() {
self.inner.fill_styled(clipped, ch, style);
}
}
}