1use duat_core::{
2 context::{self, FixedFile},
3 form::{self, Form},
4 text::{Text, text},
5 ui::{Area as UiArea, PushSpecs},
6 widgets::{Widget, WidgetCfg},
7};
8
9use crate::{Area, Ui};
10
11pub struct VertRule {
17 ff: Option<FixedFile<Ui>>,
18 text: Text,
19 sep_char: SepChar,
20}
21
22impl Widget<Ui> for VertRule {
23 type Cfg = VertRuleCfg;
24
25 fn cfg() -> Self::Cfg {
26 VertRuleCfg::new()
27 }
28
29 fn update(&mut self, area: &Area) {
30 self.text = if let Some(ff) = self.ff.as_mut()
31 && let SepChar::ThreeWay(..) | SepChar::TwoWay(..) = self.sep_char
32 {
33 let (file, _) = ff.read();
34 let lines = file.printed_lines();
35 let (upper, middle, lower) = if let Some(main) = file.cursors().get_main() {
36 let main = main.line();
37 let upper = lines.iter().filter(|&(line, _)| *line < main).count();
38 let middle = lines.iter().filter(|&(line, _)| *line == main).count();
39 let lower = lines.iter().filter(|&(line, _)| *line > main).count();
40 (upper, middle, lower)
41 } else {
42 (0, lines.len(), 0)
43 };
44
45 let chars = self.sep_char.chars();
46
47 text!(
48 [VertRule.upper] { form_string(chars[0], upper) }
49 [VertRule] { form_string(chars[1], middle) }
50 [VertRule.lower] { form_string(chars[2], lower) }
51 )
52 } else {
53 let full_line =
54 format!("{}\n", self.sep_char.chars()[1]).repeat(area.height() as usize);
55
56 text!([VertRule] full_line)
57 }
58 }
59
60 fn text(&self) -> &Text {
61 &self.text
62 }
63
64 fn text_mut(&mut self) -> &mut Text {
65 &mut self.text
66 }
67
68 fn once() -> Result<(), Text> {
69 form::set_weak("VertRule", Form::dark_grey());
70 form::id_of!("VertRule.upper", "VertRule.lower");
71 Ok(())
72 }
73}
74
75#[derive(Clone)]
78enum SepChar {
79 Uniform(char),
80 TwoWay(char, char),
82 ThreeWay(char, char, char),
84}
85
86impl SepChar {
87 fn chars(&self) -> [char; 3] {
90 match self {
91 SepChar::Uniform(uniform) => [*uniform, *uniform, *uniform],
92 SepChar::TwoWay(main, other) => [*other, *main, *other],
93 SepChar::ThreeWay(main, upper, lower) => [*upper, *main, *lower],
94 }
95 }
96}
97
98#[derive(Clone)]
100pub struct VertRuleCfg {
101 sep_char: SepChar,
102 specs: PushSpecs,
103}
104
105impl VertRuleCfg {
106 pub fn new() -> Self {
108 Self {
109 sep_char: SepChar::Uniform('│'),
110 specs: PushSpecs::left().with_hor_len(1.0),
111 }
112 }
113
114 pub fn on_the_right(self) -> Self {
115 Self {
116 specs: PushSpecs::right().with_hor_len(1.0),
117 ..self
118 }
119 }
120
121 pub fn with_char(self, char: char) -> Self {
122 Self { sep_char: SepChar::Uniform(char), ..self }
123 }
124
125 pub fn with_main_char(self, main: char) -> Self {
126 Self {
127 sep_char: match self.sep_char {
128 SepChar::Uniform(other) => SepChar::TwoWay(main, other),
129 SepChar::TwoWay(_, other) => SepChar::TwoWay(main, other),
130 SepChar::ThreeWay(_, above, below) => SepChar::ThreeWay(main, above, below),
131 },
132 ..self
133 }
134 }
135
136 pub fn with_char_above(self, above: char) -> Self {
137 Self {
138 sep_char: match self.sep_char {
139 SepChar::Uniform(other) => SepChar::ThreeWay(other, above, other),
140 SepChar::TwoWay(main, below) => SepChar::ThreeWay(main, above, below),
141 SepChar::ThreeWay(main, _, below) => SepChar::ThreeWay(main, above, below),
142 },
143 ..self
144 }
145 }
146
147 pub fn with_char_below(self, below: char) -> Self {
148 Self {
149 sep_char: match self.sep_char {
150 SepChar::Uniform(other) => SepChar::ThreeWay(other, other, below),
151 SepChar::TwoWay(main, above) => SepChar::ThreeWay(main, above, below),
152 SepChar::ThreeWay(main, above, _) => SepChar::ThreeWay(main, above, below),
153 },
154 ..self
155 }
156 }
157}
158
159impl Default for VertRuleCfg {
160 fn default() -> Self {
161 Self::new()
162 }
163}
164
165impl WidgetCfg<Ui> for VertRuleCfg {
166 type Widget = VertRule;
167
168 fn build(self, on_file: bool) -> (Self::Widget, impl Fn() -> bool, PushSpecs) {
169 let ff = on_file.then_some(context::fixed_file().unwrap());
170
171 let checker = if let Some(ff) = ff.as_ref()
172 && let SepChar::TwoWay(..) | SepChar::ThreeWay(..) = self.sep_char
173 {
174 let checker = ff.checker();
175 Box::new(checker) as Box<dyn Fn() -> bool + Send + Sync>
176 } else {
177 Box::new(move || false)
178 };
179
180 let widget = VertRule {
181 ff,
182 text: Text::default(),
183 sep_char: self.sep_char,
184 };
185
186 (widget, checker, self.specs)
187 }
188}
189
190fn form_string(char: char, count: usize) -> String {
191 [char, '\n'].repeat(count).iter().collect()
192}