use rdom_core::Dom;
use rdom_style::layout::{BorderStyle, CornerStyle};
use crate::ext::TuiExt;
use crate::render::Buffer;
use crate::render::buffer::{
BorderContribution, BorderDirState, BorderSide, DIR_E, DIR_N, DIR_S, DIR_W,
};
pub(super) fn join_borders(_dom: &Dom<TuiExt>, buf: &mut Buffer) {
let area = buf.area;
for y in area.y..area.y + area.height {
for x in area.x..area.x + area.width {
let quads = buf.half_block_quads_at(x, y);
if quads != 0 {
let glyph = half_block_quad_glyph(quads);
if !glyph.is_empty() {
let cell_state = [
buf.border_dir_at(x, y, DIR_N),
buf.border_dir_at(x, y, DIR_E),
buf.border_dir_at(x, y, DIR_S),
buf.border_dir_at(x, y, DIR_W),
];
let fg = dominant_contribution(&cell_state).fg;
if let Some(cell) = buf.cell_mut(x, y) {
cell.set_symbol(glyph);
if fg != crate::style::Color::Reset {
cell.set_fg(fg);
}
}
}
continue;
}
let cell_state = [
buf.border_dir_at(x, y, DIR_N),
buf.border_dir_at(x, y, DIR_E),
buf.border_dir_at(x, y, DIR_S),
buf.border_dir_at(x, y, DIR_W),
];
let mask = visible_mask(&cell_state);
if mask == 0 {
continue;
}
let dominant = dominant_contribution(&cell_state);
let lone = is_lone_contributor(&cell_state, dominant.priority);
if lone
&& dominant.corner_style == CornerStyle::Rounded
&& dominant.style != BorderStyle::Double
{
let rounded = ROUNDED_TABLE[mask as usize];
if !rounded.is_empty()
&& let Some(cell) = buf.cell_mut(x, y)
{
cell.set_symbol(rounded);
if dominant.fg != crate::style::Color::Reset {
cell.set_fg(dominant.fg);
}
continue;
}
}
let table = if dominant.style == BorderStyle::Double {
DOUBLE_TABLE
} else {
SOLID_TABLE
};
let replacement = table[mask as usize];
if replacement.is_empty() {
continue;
}
if let Some(cell) = buf.cell_mut(x, y) {
cell.set_symbol(replacement);
if dominant.fg != crate::style::Color::Reset {
cell.set_fg(dominant.fg);
}
}
}
}
}
fn is_lone_contributor(cell_state: &[BorderDirState; 4], priority: u64) -> bool {
for dir_state in cell_state {
if !dir_state.is_visible() {
continue;
}
let Some(c) = dir_state.winner else { continue };
if c.priority != priority {
return false;
}
}
true
}
fn visible_mask(cell_state: &[BorderDirState; 4]) -> u8 {
let mut mask = 0u8;
if cell_state[DIR_N].is_visible() {
mask |= 0b0001;
}
if cell_state[DIR_E].is_visible() {
mask |= 0b0010;
}
if cell_state[DIR_S].is_visible() {
mask |= 0b0100;
}
if cell_state[DIR_W].is_visible() {
mask |= 0b1000;
}
mask
}
fn dominant_contribution(cell_state: &[BorderDirState; 4]) -> BorderContribution {
let mut best: Option<BorderContribution> = None;
for dir_state in cell_state {
if !dir_state.is_visible() {
continue;
}
let Some(c) = dir_state.winner else { continue };
let win = match best {
None => true,
Some(prev) => (c.style.rank(), c.priority) > (prev.style.rank(), prev.priority),
};
if win {
best = Some(c);
}
}
best.unwrap_or(BorderContribution {
style: BorderStyle::Solid,
fg: crate::style::Color::Reset,
priority: 0,
corner_style: CornerStyle::Square,
side: BorderSide::Top,
})
}
const HALF_BLOCK_QUAD_TABLE: [&str; 16] = [
" ", "▘", "▝", "▀", "▖", "▌", "▞", "▛", "▗", "▚", "▐", "▜", "▄", "▙", "▟", "█", ];
fn half_block_quad_glyph(quads: u8) -> &'static str {
if quads == 0 {
return "";
}
HALF_BLOCK_QUAD_TABLE[(quads & 0b1111) as usize]
}
const SOLID_TABLE: [&str; 16] = [
"", "│", "─", "└", "│", "│", "┌", "├", "─", "┘", "─", "┴", "┐", "┤", "┬", "┼", ];
const DOUBLE_TABLE: [&str; 16] = [
"", "║", "═", "╚", "║", "║", "╔", "╠", "═", "╝", "═", "╩", "╗", "╣", "╦", "╬", ];
const ROUNDED_TABLE: [&str; 16] = [
"", "", "", "╰", "", "", "╭", "", "", "╯", "", "", "╮", "", "", "", ];