use crate::layout::{Border, LayoutRect};
use crate::render::buffer::{BorderContribution, BorderSide, DIR_E, DIR_N, DIR_S, DIR_W};
use crate::render::{Buffer, Modifier, Rect, Style};
use crate::style::Color;
use rdom_style::layout::BorderStyle;
#[allow(dead_code)]
const SINGLE_TL: &str = "┌";
#[allow(dead_code)]
const SINGLE_TR: &str = "┐";
#[allow(dead_code)]
const SINGLE_BL: &str = "└";
#[allow(dead_code)]
const SINGLE_BR: &str = "┘";
#[allow(dead_code)]
const ROUNDED_TL: &str = "╭";
#[allow(dead_code)]
const ROUNDED_TR: &str = "╮";
#[allow(dead_code)]
const ROUNDED_BL: &str = "╰";
#[allow(dead_code)]
const ROUNDED_BR: &str = "╯";
#[allow(dead_code)]
const HORIZONTAL: &str = "─";
#[allow(dead_code)]
const VERTICAL: &str = "│";
pub(super) fn fill_bg(buf: &mut Buffer, area: Rect, bg: Color, opacity: f32) {
let opaque = opacity >= 1.0;
for y in area.y..area.bottom() {
for x in area.x..area.right() {
if opaque {
clear_cell_for_opaque_fill(buf, x, y);
clear_border_dirs(buf, x, y);
if let Some(cell) = buf.cell_mut(x, y) {
cell.bg = bg;
}
} else {
let blended = buf.compose_bg_for_cell(x, y, bg);
if let Some(cell) = buf.cell_mut(x, y) {
cell.bg = blended;
}
}
}
}
}
fn clear_border_dirs(buf: &mut Buffer, x: u16, y: u16) {
use crate::render::buffer::BorderDirState;
for dir in 0..4 {
buf.set_border_dir(x, y, dir, BorderDirState::default());
}
}
fn clear_cell_for_opaque_fill(buf: &mut Buffer, x: u16, y: u16) {
let (also_clear_x, primary_side): (Option<u16>, _) = match buf.cell(x, y) {
Some(c) if c.is_spacer() => (x.checked_sub(1), "spacer's primary"),
Some(c) if c.cell_width() == 2 => (Some(x.saturating_add(1)), "wide-glyph spacer"),
_ => (None, ""),
};
let _ = primary_side; if let Some(partner_x) = also_clear_x
&& let Some(partner) = buf.cell_mut(partner_x, y)
{
partner.set_symbol(" ");
partner.fg = Color::Reset;
partner.modifier = Modifier::empty();
}
if let Some(cell) = buf.cell_mut(x, y) {
cell.set_symbol(" ");
cell.fg = Color::Reset;
cell.modifier = Modifier::empty();
}
}
pub(super) fn paint_border(
buf: &mut Buffer,
outer: LayoutRect,
border: Border,
border_fg: Color,
clip: Rect,
priority: u64,
) {
let _ = Style::new();
let top = border.top;
let bot = border.bottom;
let lft = border.left;
let rgt = border.right;
let any_top = !top.is_none();
let any_bot = !bot.is_none();
let any_lft = !lft.is_none();
let any_rgt = !rgt.is_none();
if !(any_top || any_bot || any_lft || any_rgt) {
return;
}
let right_x = outer.x + outer.width as i32 - 1;
let bottom_y = outer.y + outer.height as i32 - 1;
const NM: u8 = 0b0001;
const EM: u8 = 0b0010;
const SM: u8 = 0b0100;
const WM: u8 = 0b1000;
let buf_area = buf.area;
let off_buffer = |x: u16, y: u16, bits: u8| -> u8 {
let mut out = bits;
if y == buf_area.y {
out &= !NM;
}
if x + 1 >= buf_area.x + buf_area.width {
out &= !EM;
}
if y + 1 >= buf_area.y + buf_area.height {
out &= !SM;
}
if x == buf_area.x {
out &= !WM;
}
out
};
if any_top && in_clip_row(outer.y, clip) {
let y = outer.y as u16;
for x in outer.x..=right_x {
if x < 0 {
continue;
}
let xu = x as u16;
if !clip.contains(xu, y) {
continue;
}
let bits = if any_lft && x == outer.x {
EM | SM
} else if any_rgt && x == right_x {
WM | SM
} else {
EM | WM
};
let allowed = off_buffer(xu, y, bits);
if allowed & EM != 0 {
add_dir(
buf,
xu,
y,
DIR_E,
top,
border_fg,
priority,
border.corner_style,
BorderSide::Top,
);
}
if allowed & WM != 0 {
add_dir(
buf,
xu,
y,
DIR_W,
top,
border_fg,
priority,
border.corner_style,
BorderSide::Top,
);
}
if allowed & SM != 0 {
let (style, src_side) = if x == outer.x {
(lft, BorderSide::Left)
} else {
(rgt, BorderSide::Right)
};
add_dir(
buf,
xu,
y,
DIR_S,
style,
border_fg,
priority,
border.corner_style,
src_side,
);
}
}
}
if any_bot && in_clip_row(bottom_y, clip) && outer.height >= 2 {
let y = bottom_y as u16;
for x in outer.x..=right_x {
if x < 0 {
continue;
}
let xu = x as u16;
if !clip.contains(xu, y) {
continue;
}
let bits = if any_lft && x == outer.x {
NM | EM
} else if any_rgt && x == right_x {
NM | WM
} else {
EM | WM
};
let allowed = off_buffer(xu, y, bits);
if allowed & EM != 0 {
add_dir(
buf,
xu,
y,
DIR_E,
bot,
border_fg,
priority,
border.corner_style,
BorderSide::Bottom,
);
}
if allowed & WM != 0 {
add_dir(
buf,
xu,
y,
DIR_W,
bot,
border_fg,
priority,
border.corner_style,
BorderSide::Bottom,
);
}
if allowed & NM != 0 {
let (style, src_side) = if x == outer.x {
(lft, BorderSide::Left)
} else {
(rgt, BorderSide::Right)
};
add_dir(
buf,
xu,
y,
DIR_N,
style,
border_fg,
priority,
border.corner_style,
src_side,
);
}
}
}
if any_lft && in_clip_col(outer.x, clip) {
let x = outer.x as u16;
let y_start = if any_top { outer.y + 1 } else { outer.y };
let y_end = if any_bot { bottom_y - 1 } else { bottom_y };
for y in y_start..=y_end {
if y < 0 {
continue;
}
let yu = y as u16;
if !clip.contains(x, yu) {
continue;
}
let allowed = off_buffer(x, yu, NM | SM);
if allowed & NM != 0 {
add_dir(
buf,
x,
yu,
DIR_N,
lft,
border_fg,
priority,
border.corner_style,
BorderSide::Left,
);
}
if allowed & SM != 0 {
add_dir(
buf,
x,
yu,
DIR_S,
lft,
border_fg,
priority,
border.corner_style,
BorderSide::Left,
);
}
}
}
if any_rgt && in_clip_col(right_x, clip) && outer.width >= 2 {
let x = right_x as u16;
let y_start = if any_top { outer.y + 1 } else { outer.y };
let y_end = if any_bot { bottom_y - 1 } else { bottom_y };
for y in y_start..=y_end {
if y < 0 {
continue;
}
let yu = y as u16;
if !clip.contains(x, yu) {
continue;
}
let allowed = off_buffer(x, yu, NM | SM);
if allowed & NM != 0 {
add_dir(
buf,
x,
yu,
DIR_N,
rgt,
border_fg,
priority,
border.corner_style,
BorderSide::Right,
);
}
if allowed & SM != 0 {
add_dir(
buf,
x,
yu,
DIR_S,
rgt,
border_fg,
priority,
border.corner_style,
BorderSide::Right,
);
}
}
}
}
#[inline]
#[allow(clippy::too_many_arguments)]
fn add_dir(
buf: &mut Buffer,
x: u16,
y: u16,
dir: usize,
style: BorderStyle,
fg: Color,
priority: u64,
corner_style: crate::layout::CornerStyle,
side: BorderSide,
) {
if style.is_none() {
return;
}
buf.add_border_dir(
x,
y,
dir,
BorderContribution {
style,
fg,
priority,
corner_style,
side,
},
);
}
#[inline]
fn in_clip_row(y_signed: i32, clip: Rect) -> bool {
y_signed >= clip.y as i32 && y_signed < clip.bottom() as i32
}
#[inline]
fn in_clip_col(x_signed: i32, clip: Rect) -> bool {
x_signed >= clip.x as i32 && x_signed < clip.right() as i32
}