#![cfg(feature = "alloc")]
mod cache;
mod state;
mod wrap;
use core::fmt::Debug;
use core::iter;
use core::marker::PhantomData;
use self::cache::{CacheItem, CacheKey};
use self::state::State;
use embedded_graphics::draw_target::DrawTarget;
use ratatui_core::backend::{Backend, ClearType, WindowSize};
use ratatui_core::buffer::Cell;
use ratatui_core::layout::{Position, Size};
use ratatui_core::style::Modifier;
use crate::backend::DrawTargetBackend;
use crate::blink::{Blink, Blinked, ControlBlinking};
use crate::cursor::{Colors, Extent, Symbol};
use crate::error::Error;
use super::traits;
use super::{WrapTrait, Wrapper};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
backend: B,
slow_blink: Blink,
rapid_blink: Blink,
state: State,
phantom: PhantomData<D>,
}
pub trait ConfigureBlinkWrapper {
fn slow_blink(&self) -> Blink;
fn set_slow_blink(&mut self, slow_blink: Blink);
fn rapid_blink(&self) -> Blink;
fn set_rapid_blink(&mut self, rapid_blink: Blink);
}
impl<B, D> BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
pub const fn new(backend: B, slow_blink: Blink, rapid_blink: Blink) -> Self {
Self {
backend,
slow_blink,
rapid_blink,
state: State::new(),
phantom: PhantomData,
}
}
}
impl<B, D> Backend for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
type Error = B::Error;
fn draw<'z, I>(&mut self, content: I) -> Result<(), Self::Error>
where
I: Iterator<Item = (u16, u16, &'z Cell)>,
{
use unicode_width::UnicodeWidthStr;
const SPARSE_BLINK: Modifier = Modifier::SLOW_BLINK.union(Modifier::RAPID_BLINK);
const ALL_BLINK: Modifier = SPARSE_BLINK;
let slow_blink = self.slow_blink.get(self.state.ticks);
let rapid_blink = self.rapid_blink.get(self.state.ticks);
let sparse_blink = Blinked(slow_blink.0 && rapid_blink.0);
let cursor_position = self.get_cursor_position()?;
let previous_slow_blink = self.slow_blink.get(self.state.ticks.wrapping_sub(1));
let previous_rapid_blink = self.rapid_blink.get(self.state.ticks.wrapping_sub(1));
let previous_sparse_blink = Blinked(previous_slow_blink.0 && previous_rapid_blink.0);
let content = content.inspect(|&(x, y, cell)| {
let key = CacheKey::new(x, y);
if cell.modifier.intersects(ALL_BLINK) {
let cell = cell.clone();
let tickstamp = self.state.ticks;
self.state.cache.insert_or_replace(key, cell, tickstamp);
} else {
self.state.cache.remove(&key);
}
});
let no_blink_content = content.filter(|(_, _, cell)| !cell.modifier.intersects(ALL_BLINK));
self.backend.draw(no_blink_content)?;
for (blink, previous_blink, blink_flags) in [
(slow_blink, previous_slow_blink, Modifier::SLOW_BLINK),
(rapid_blink, previous_rapid_blink, Modifier::RAPID_BLINK),
(sparse_blink, previous_sparse_blink, SPARSE_BLINK),
] {
let blink_changed = blink != previous_blink;
let blink_content = self.state.cache.iter().filter_map(|item| {
let CacheItem {
key: CacheKey { x, y },
ref cell,
tickstamp,
} = *item;
let is_relevant = blink_flags == cell.modifier.intersection(ALL_BLINK);
if is_relevant && (blink_changed || tickstamp == self.state.ticks) {
let end = cell.symbol().width();
if y == cursor_position.y {
for right in (0..end)
.filter_map(|x_offset| x_offset.try_into().ok())
.filter_map(|x_offset| x.checked_add(x_offset))
{
if right == cursor_position.x {
self.state.cursor_dirty.replace(());
}
}
}
Some((x, y, cell))
} else {
None
}
});
if blink == Blinked(true) {
self.backend.draw_hidden(blink_content)?;
} else {
self.backend.draw(blink_content)?;
}
}
if self.state.blinking {
self.advance_blink_by(1)?;
}
Ok(())
}
fn hide_cursor(&mut self) -> Result<(), Self::Error> {
self.backend.hide_cursor()
}
fn show_cursor(&mut self) -> Result<(), Self::Error> {
self.backend.show_cursor()
}
fn get_cursor_position(&mut self) -> Result<Position, Self::Error> {
self.backend.get_cursor_position()
}
fn set_cursor_position<P: Into<Position>>(&mut self, position: P) -> Result<(), Self::Error> {
self.backend.set_cursor_position(position)
}
fn clear(&mut self) -> Result<(), Self::Error> {
self.backend.clear()?;
self.state.cache.clear();
Ok(())
}
fn clear_region(&mut self, clear_type: ClearType) -> Result<(), Self::Error> {
let cursor_position = self.get_cursor_position()?;
let [x, y] = {
let Position { x, y } = cursor_position;
if let Some((x, y, _)) = self.state.cache.find(cursor_position) {
[x, y]
} else {
[x, y]
}
};
let cursor_is_below_or_right = |key: CacheKey| y > key.y || y == key.y && x > key.x;
let cursor_is_above_or_left = |key: CacheKey| y < key.y || y == key.y && x < key.x;
let cursor_is_above_or_below = |key: CacheKey| y != key.y;
let cursor_is_above_or_below_or_right = |key: CacheKey| y != key.y || x > key.x;
self.backend.clear_region(clear_type)?;
match clear_type {
ClearType::All => self.state.cache.clear(),
ClearType::AfterCursor => self.state.cache.retain(cursor_is_below_or_right),
ClearType::BeforeCursor => self.state.cache.retain(cursor_is_above_or_left),
ClearType::CurrentLine => self.state.cache.retain(cursor_is_above_or_below),
ClearType::UntilNewLine => self.state.cache.retain(cursor_is_above_or_below_or_right),
};
Ok(())
}
fn size(&self) -> Result<Size, Self::Error> {
self.backend.size()
}
fn window_size(&mut self) -> Result<WindowSize, Self::Error> {
self.backend.window_size()
}
fn flush(&mut self) -> Result<(), Self::Error> {
self.backend.flush()
}
}
impl<B, D> DrawTargetBackend<D> for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
fn call(&mut self, f: impl FnMut(&mut D) -> Result<(), D::Error>) -> Result<(), D::Error> {
self.backend.call(f)
}
fn draw_hidden<'z, I>(&mut self, content: I) -> Result<(), Self::Error>
where
I: Iterator<Item = (u16, u16, &'z Cell)>,
{
self.backend.draw_hidden(content)
}
fn draw_cursor<'z, I>(
&mut self,
content: I,
colors: Colors,
extent: Extent,
symbol: Symbol,
) -> Result<(), Self::Error>
where
I: Iterator<Item = (u16, u16, &'z Cell)>,
{
const SPARSE_BLINK: Modifier = Modifier::SLOW_BLINK.union(Modifier::RAPID_BLINK);
const ALL_BLINK: Modifier = SPARSE_BLINK;
let slow_blink = self.slow_blink.get(self.state.ticks);
let rapid_blink = self.rapid_blink.get(self.state.ticks);
let sparse_blink = Blinked(slow_blink.0 && rapid_blink.0);
for (x, y, cell) in content {
let blink_flags = cell.modifier.intersection(ALL_BLINK);
let is_hidden = blink_flags == Modifier::SLOW_BLINK && slow_blink == Blinked(true)
|| blink_flags == Modifier::RAPID_BLINK && rapid_blink == Blinked(true)
|| blink_flags == SPARSE_BLINK && sparse_blink == Blinked(true);
let clone = if is_hidden {
let mut cell = cell.clone();
cell.modifier = cell.modifier.union(Modifier::HIDDEN);
Some(cell)
} else {
None
};
let cell = clone.as_ref().unwrap_or(cell);
let content = iter::once((x, y, cell));
self.backend.draw_cursor(content, colors, extent, symbol)?;
}
Ok(())
}
fn advance_blink_by(&mut self, ticks: usize) -> Result<(), Self::Error> {
self.state.ticks = self.state.ticks.wrapping_add(ticks);
self.backend.advance_blink_by(ticks)
}
fn take_dirty_cursor(&mut self) -> Result<Option<()>, Self::Error> {
let units = [
self.backend.take_dirty_cursor()?,
self.state.cursor_dirty.take(),
];
let unit = units.into_iter().flatten().next();
Ok(unit)
}
}
impl<B, D> ConfigureBlinkWrapper for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
fn slow_blink(&self) -> Blink {
self.slow_blink
}
fn set_slow_blink(&mut self, slow_blink: Blink) {
self.slow_blink = slow_blink;
}
fn rapid_blink(&self) -> Blink {
self.rapid_blink
}
fn set_rapid_blink(&mut self, rapid_blink: Blink) {
self.rapid_blink = rapid_blink;
}
}
impl<B, D> ControlBlinking<D> for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
fn blinking(&self) -> bool {
self.state.blinking
}
fn start_blinking(&mut self) {
self.state.blinking = true;
}
fn stop_blinking(&mut self) {
self.state.blinking = false;
}
}
impl<B, D> Wrapper for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
type Inner = B;
fn inner(&self) -> &Self::Inner {
&self.backend
}
fn inner_mut(&mut self) -> &mut Self::Inner {
&mut self.backend
}
fn into_inner(self) -> Self::Inner {
self.backend
}
}
impl<B, D> WrapTrait<traits::ConfigureBackend> for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
}
impl<B, D> WrapTrait<traits::ConfigureCursorWrapper> for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
}
impl<B, D> WrapTrait<traits::ControlCursorBlinking> for BlinkWrapper<B, D>
where
B: DrawTargetBackend<D, Error = Error<D::Error>>,
D: DrawTarget,
D::Error: Debug,
{
}
impl<W, B> ConfigureBlinkWrapper for W
where
B: ConfigureBlinkWrapper,
W: WrapTrait<traits::ConfigureBlinkWrapper, Inner = B>,
{
fn slow_blink(&self) -> Blink {
self.inner().slow_blink()
}
fn set_slow_blink(&mut self, slow_blink: Blink) {
self.inner_mut().set_slow_blink(slow_blink);
}
fn rapid_blink(&self) -> Blink {
self.inner().rapid_blink()
}
fn set_rapid_blink(&mut self, rapid_blink: Blink) {
self.inner_mut().set_rapid_blink(rapid_blink);
}
}