1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
|
use super::{
is_too_high, parse_indices, parse_opaque, parse_selected, parse_text, stringify_text,
Coordinates, Text,
};
use crate::panes::terminal_character::{AnsiCode, CharacterStyles, RESET_STYLES};
use zellij_utils::data::Style;
use unicode_width::UnicodeWidthChar;
#[derive(Debug, Clone)]
pub struct NestedListItem {
pub text: Text,
pub indentation_level: usize,
}
pub fn nested_list(
mut contents: Vec<NestedListItem>,
style: &Style,
coordinates: Option<Coordinates>,
) -> Vec<u8> {
let mut stringified = String::new();
let max_width = coordinates
.as_ref()
.and_then(|c| c.width)
.unwrap_or_else(|| max_nested_item_width(&contents));
for (line_index, line_item) in contents.drain(..).enumerate() {
if is_too_high(line_index + 1, &coordinates) {
break;
}
let style_declaration = if line_item.text.selected {
style.colors.list_selected
} else {
style.colors.list_unselected
};
let padding = line_item.indentation_level * 2 + 1;
let bulletin = if line_item.indentation_level % 2 == 0 {
"> "
} else {
"- "
};
let text_style = if line_item.text.opaque || line_item.text.selected {
CharacterStyles::from(style_declaration)
.background(Some(style_declaration.background.into()))
} else {
CharacterStyles::from(style_declaration)
};
let (mut text, text_width) = stringify_text(
&line_item.text,
Some(padding + bulletin.len()),
&coordinates,
&style_declaration,
text_style.bold(Some(AnsiCode::On)),
);
text = pad_line(text, max_width, padding, text_width);
let go_to_row_instruction = coordinates
.as_ref()
.map(|c| c.stringify_with_y_offset(line_index))
.unwrap_or_else(|| {
if line_index != 0 {
format!("\n\r")
} else {
"".to_owned()
}
});
stringified.push_str(&format!(
"{}{}{:padding$}{bulletin}{}{text}{}",
go_to_row_instruction,
text_style,
" ",
text_style.bold(Some(AnsiCode::On)),
RESET_STYLES
));
}
stringified.as_bytes().to_vec()
}
pub fn parse_nested_list_items<'a>(
params_iter: impl Iterator<Item = &'a mut String>,
) -> Vec<NestedListItem> {
params_iter
.flat_map(|mut stringified| {
let indentation_level = parse_indentation_level(&mut stringified);
let selected = parse_selected(&mut stringified);
let opaque = parse_opaque(&mut stringified);
let indices = parse_indices(&mut stringified);
let text = parse_text(&mut stringified).map_err(|e| e.to_string())?;
let text = Text {
text,
opaque,
selected,
indices,
};
Ok::<NestedListItem, String>(NestedListItem {
text,
indentation_level,
})
})
.collect::<Vec<NestedListItem>>()
}
fn parse_indentation_level(stringified: &mut String) -> usize {
let mut indentation_level = 0;
loop {
if stringified.is_empty() {
break;
}
if stringified.chars().next() == Some('|') {
stringified.remove(0);
indentation_level += 1;
} else {
break;
}
}
indentation_level
}
fn max_nested_item_width(contents: &Vec<NestedListItem>) -> usize {
let mut width_of_longest_line = 0;
for line_item in contents.iter() {
let mut line_item_text_width = 0;
for character in line_item.text.text.chars() {
let character_width = character.width().unwrap_or(0);
line_item_text_width += character_width;
}
let bulletin_width = 2;
let padding = line_item.indentation_level * 2 + 1;
let total_width = line_item_text_width + bulletin_width + padding;
if width_of_longest_line < total_width {
width_of_longest_line = total_width;
}
}
width_of_longest_line
}
fn pad_line(text: String, max_width: usize, padding: usize, text_width: usize) -> String {
if max_width > text_width + padding + 2 {
// 2 is the bulletin
let end_padding = max_width.saturating_sub(text_width + padding + 2);
return format!("{}{:end_padding$}", text, " ");
}
text
}
|