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 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 dominant.style == BorderStyle::HalfBlock && lone {
let glyph = half_block_glyph(&cell_state, mask);
if !glyph.is_empty()
&& let Some(cell) = buf.cell_mut(x, y)
{
cell.set_symbol(glyph);
if dominant.fg != crate::style::Color::Reset {
cell.set_fg(dominant.fg);
}
}
continue;
}
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 SOLID_TABLE: [&str; 16] = [
"", "│", "─", "└", "│", "│", "┌", "├", "─", "┘", "─", "┴", "┐", "┤", "┬", "┼", ];
const DOUBLE_TABLE: [&str; 16] = [
"", "║", "═", "╚", "║", "║", "╔", "╠", "═", "╝", "═", "╩", "╗", "╣", "╦", "╬", ];
const ROUNDED_TABLE: [&str; 16] = [
"", "", "", "╰", "", "", "╭", "", "", "╯", "", "", "╮", "", "", "", ];
fn half_block_glyph(cell_state: &[BorderDirState; 4], mask: u8) -> &'static str {
let side = |dir: usize| -> Option<BorderSide> { cell_state[dir].winner.map(|c| c.side) };
match mask {
0b1010 => match side(DIR_E).or_else(|| side(DIR_W)) {
Some(BorderSide::Top) => "▄",
Some(BorderSide::Bottom) => "▀",
_ => "",
},
0b0101 => match side(DIR_N).or_else(|| side(DIR_S)) {
Some(BorderSide::Left) => "▐",
Some(BorderSide::Right) => "▌",
_ => "",
},
0b0110 => "▗",
0b1100 => "▖",
0b0011 => "▝",
0b1001 => "▘",
_ => "",
}
}