use duat_core::{
context::Handle,
data::Pass,
hook::{self, BufferUpdated},
text::{Text, TextMut, txt},
ui::{PushSpecs, PushTarget, Side, Widget},
};
pub fn add_vertrule_hook() {
hook::add::<BufferUpdated>(|pa, buffer| {
for (vertrule, _) in buffer.get_related::<VertRule>(pa) {
let (buf, vr, area) = (buffer.read(pa), &vertrule.read(pa), vertrule.area());
let text = if let SepChar::ThreeWay(..) | SepChar::TwoWay(..) = vr.sep_char {
let lines = buffer.printed_line_numbers(pa);
let (upper, middle, lower) = {
if let Some(main) = buf.selections().get_main() {
let main = main.caret().line();
let upper = lines.iter().filter(|&line| line.number < main).count();
let middle = lines.iter().filter(|&line| line.number == main).count();
let lower = lines.iter().filter(|&line| line.number > main).count();
(upper, middle, lower)
} else {
(0, lines.len(), 0)
}
};
let chars = vr.sep_char.chars();
txt!(
"[rule.upper]{}[]{}[rule.lower]{}",
form_string(chars[0], upper),
form_string(chars[1], middle),
form_string(chars[2], lower)
)
} else {
let full_line =
format!("{}\n", vr.sep_char.chars()[1]).repeat(area.height(pa) as usize);
txt!("{full_line}")
};
vertrule.write(pa).text = text;
}
});
}
pub struct VertRule {
text: Text,
pub sep_char: SepChar,
}
impl VertRule {
pub fn builder() -> VertRuleBuilder {
VertRuleBuilder::default()
}
}
impl Widget for VertRule {
fn text(&self) -> &Text {
&self.text
}
fn text_mut(&mut self) -> TextMut<'_> {
self.text.as_mut()
}
}
#[derive(Default, Clone)]
pub struct VertRuleBuilder {
pub sep_char: SepChar,
pub on_the_right: bool,
}
impl VertRuleBuilder {
pub fn push_on(self, pa: &mut Pass, push_target: &impl PushTarget) -> Handle<VertRule> {
let vert_rule = VertRule {
text: Text::default(),
sep_char: self.sep_char,
};
let specs = PushSpecs {
side: if self.on_the_right {
Side::Right
} else {
Side::Left
},
width: Some(1.0),
..Default::default()
};
push_target.push_outer(pa, vert_rule, specs)
}
}
#[derive(Clone)]
pub enum SepChar {
Uniform(char),
TwoWay(char, char),
ThreeWay(char, char, char),
}
impl SepChar {
fn chars(&self) -> [char; 3] {
match self {
SepChar::Uniform(uniform) => [*uniform, *uniform, *uniform],
SepChar::TwoWay(main, other) => [*other, *main, *other],
SepChar::ThreeWay(main, upper, lower) => [*upper, *main, *lower],
}
}
}
impl Default for SepChar {
fn default() -> Self {
Self::Uniform(' ')
}
}
fn form_string(char: char, count: usize) -> String {
[char, '\n'].repeat(count).iter().collect()
}