1use duat_core::{
10 context::Handle,
11 data::Pass,
12 text::{Text, txt},
13 ui::{PushSpecs, PushTarget, Side, Widget},
14};
15
16pub struct VertRule {
34 handle: Option<Handle>,
35 text: Text,
36 pub sep_char: SepChar,
37}
38
39impl VertRule {
40 pub fn builder() -> VertRuleBuilder {
43 VertRuleBuilder::default()
44 }
45}
46
47impl Widget for VertRule {
48 fn update(pa: &mut Pass, handle: &Handle<Self>) {
49 let vr = handle.read(pa);
50 let text = if let Some(handle) = vr.handle.as_ref()
51 && let SepChar::ThreeWay(..) | SepChar::TwoWay(..) = vr.sep_char
52 {
53 let (upper, middle, lower) = {
54 let file = handle.read(pa);
55
56 let lines = file.printed_lines();
57 if let Some(main) = file.selections().get_main() {
58 let main = main.line();
59 let upper = lines.iter().filter(|&(line, _)| *line < main).count();
60 let middle = lines.iter().filter(|&(line, _)| *line == main).count();
61 let lower = lines.iter().filter(|&(line, _)| *line > main).count();
62 (upper, middle, lower)
63 } else {
64 (0, lines.len(), 0)
65 }
66 };
67
68 let chars = vr.sep_char.chars();
69 txt!(
70 "[rule.upper]{}[]{}[rule.lower]{}",
71 form_string(chars[0], upper),
72 form_string(chars[1], middle),
73 form_string(chars[2], lower)
74 )
75 } else {
76 let full_line =
77 format!("{}\n", vr.sep_char.chars()[1]).repeat(handle.area().height(pa) as usize);
78
79 txt!("{full_line}")
80 };
81
82 handle.write(pa).text = text;
83 }
84
85 fn needs_update(&self, pa: &Pass) -> bool {
86 matches!(self.sep_char, SepChar::ThreeWay(..) | SepChar::TwoWay(..))
87 && self.handle.as_ref().is_some_and(|fh| fh.has_changed(pa))
88 }
89
90 fn text(&self) -> &Text {
91 &self.text
92 }
93
94 fn text_mut(&mut self) -> &mut Text {
95 &mut self.text
96 }
97}
98
99#[derive(Default, Clone)]
101pub struct VertRuleBuilder {
102 pub sep_char: SepChar,
103 pub on_the_right: bool,
104}
105
106impl VertRuleBuilder {
107 pub fn push_on(self, pa: &mut Pass, push_target: &impl PushTarget) -> Handle<VertRule> {
109 let vert_rule = VertRule {
110 handle: push_target.try_downcast(),
111 text: Text::default(),
112 sep_char: self.sep_char,
113 };
114
115 let specs = PushSpecs {
116 side: if self.on_the_right {
117 Side::Right
118 } else {
119 Side::Left
120 },
121 width: Some(1.0),
122 ..Default::default()
123 };
124
125 push_target.push_outer(pa, vert_rule, specs)
126 }
127}
128
129#[derive(Clone)]
132pub enum SepChar {
133 Uniform(char),
134 TwoWay(char, char),
136 ThreeWay(char, char, char),
138}
139
140impl SepChar {
141 fn chars(&self) -> [char; 3] {
144 match self {
145 SepChar::Uniform(uniform) => [*uniform, *uniform, *uniform],
146 SepChar::TwoWay(main, other) => [*other, *main, *other],
147 SepChar::ThreeWay(main, upper, lower) => [*upper, *main, *lower],
148 }
149 }
150}
151
152impl Default for SepChar {
153 fn default() -> Self {
154 Self::Uniform('│')
155 }
156}
157
158fn form_string(char: char, count: usize) -> String {
159 [char, '\n'].repeat(count).iter().collect()
160}