* feat(plugins): add transparent background for text and nested_list * chore: fix formatting issue * feat: invert flag behaviour * feat: implement bg_black handling for table cells * fix: order of selected and bg_black in protocol * chore: rename from bg_black to opaque * fix: explicit selected, if opaque and selected for text * chore: fix formatting issues * feat: opaque tab-bar * feat: opaque session-manager bars * feat: opaque ribbon in plugin manager * feat: opaque one-line ui * feat: opaque tab-bar in configuration plugin
157 lines
5.3 KiB
Rust
157 lines
5.3 KiB
Rust
use super::{
|
|
is_too_high, parse_indices, parse_opaque, parse_selected, parse_text, stringify_text,
|
|
Coordinates, Text,
|
|
};
|
|
use crate::panes::terminal_character::{AnsiCode, 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 mut reset_styles_for_item = RESET_STYLES;
|
|
reset_styles_for_item.background = None;
|
|
reset_styles_for_item.foreground = None;
|
|
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.selected {
|
|
reset_styles_for_item
|
|
.bold(Some(AnsiCode::On))
|
|
.foreground(Some(style.colors.white.into()))
|
|
.background(Some(style.colors.bg.into()))
|
|
} else if line_item.text.opaque {
|
|
reset_styles_for_item
|
|
.bold(Some(AnsiCode::On))
|
|
.foreground(Some(style.colors.white.into()))
|
|
.background(Some(style.colors.black.into()))
|
|
} else {
|
|
reset_styles_for_item
|
|
.bold(Some(AnsiCode::On))
|
|
.foreground(Some(style.colors.white.into()))
|
|
};
|
|
let (mut text, text_width) = stringify_text(
|
|
&line_item.text,
|
|
Some(padding + bulletin.len()),
|
|
&coordinates,
|
|
style,
|
|
text_style,
|
|
line_item.text.selected,
|
|
);
|
|
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()
|
|
}
|
|
});
|
|
let line_style = if line_item.text.selected {
|
|
RESET_STYLES
|
|
.foreground(Some(style.colors.white.into()))
|
|
.background(Some(style.colors.bg.into()))
|
|
} else if line_item.text.opaque {
|
|
RESET_STYLES
|
|
.foreground(Some(style.colors.white.into()))
|
|
.background(Some(style.colors.black.into()))
|
|
} else {
|
|
RESET_STYLES.foreground(Some(style.colors.white.into()))
|
|
};
|
|
stringified.push_str(&format!(
|
|
"{}{}{}{:padding$}{bulletin}{}{text}{}",
|
|
go_to_row_instruction, line_style, reset_styles_for_item, " ", text_style, 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
|
|
}
|