use embedded_graphics::{geometry::Point, primitives::Rectangle, text::LineHeight};
use az::SaturatingAs;
#[derive(Debug, Clone)]
pub struct LineCursor {
start: Point,
width: u32,
position: u32,
tab_width: u32,
}
impl LineCursor {
pub const fn new(width: u32, tab_width: u32) -> Self {
Self {
start: Point::zero(),
width,
tab_width,
position: 0,
}
}
pub fn pos(&self) -> Point {
self.start + Point::new(self.position.saturating_as(), 0)
}
pub const fn next_tab_width(&self) -> u32 {
let next_tab_pos = if self.tab_width == 0 {
self.position
} else {
(self.position / self.tab_width + 1) * self.tab_width
};
next_tab_pos - self.position
}
pub const fn line_width(&self) -> u32 {
self.width
}
pub const fn fits_in_line(&self, width: u32) -> bool {
width <= self.space()
}
pub const fn space(&self) -> u32 {
self.width - self.position
}
pub fn move_cursor(&mut self, by: i32) -> Result<i32, i32> {
if by < 0 {
let abs = by.unsigned_abs();
if abs <= self.position {
self.position -= abs;
Ok(by)
} else {
Err(-(self.position as i32))
}
} else {
let space = self.space() as i32;
if by <= space {
self.position += by as u32;
Ok(by)
} else {
Err(space)
}
}
}
pub fn move_cursor_forward(&mut self, by: u32) -> Result<u32, u32> {
let space = self.space();
if by <= space {
self.position += by;
Ok(by)
} else {
Err(space)
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct Cursor {
pub y: i32,
top_left: Point,
bottom: i32,
line_width: u32,
line_height: u32,
line_spacing: i32,
tab_width: u32,
}
impl Cursor {
#[inline]
#[must_use]
pub fn new(
bounds: Rectangle,
base_line_height: u32,
line_height: LineHeight,
tab_width: u32,
) -> Self {
Self {
y: bounds.top_left.y,
top_left: bounds.top_left,
bottom: bounds.top_left.y + bounds.size.height.saturating_as::<i32>()
- base_line_height.saturating_as::<i32>(),
line_width: bounds.size.width,
line_height: base_line_height,
line_spacing: line_height.to_absolute(base_line_height).saturating_as(),
tab_width,
}
}
#[must_use]
pub(crate) fn line(&self) -> LineCursor {
LineCursor {
start: self.line_start(),
width: self.line_width,
position: 0,
tab_width: self.tab_width,
}
}
#[inline]
pub(crate) fn line_start(&self) -> Point {
Point::new(self.top_left.x, self.y)
}
#[inline]
pub fn bottom(&self) -> i32 {
self.bottom
}
#[inline]
pub fn top_left(&self) -> Point {
self.top_left
}
#[inline]
pub fn line_width(&self) -> u32 {
self.line_width
}
#[inline]
pub fn line_height(&self) -> u32 {
self.line_height
}
#[inline]
pub fn new_line(&mut self) {
self.y += self.line_spacing;
}
#[inline]
#[must_use]
pub fn in_display_area(&self) -> bool {
self.top_left.y <= self.y && self.y <= self.bottom
}
}