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