summaryrefslogtreecommitdiffstats
path: root/zellij-server/src/ui
diff options
context:
space:
mode:
authorAram Drevekenin <aram@poor.dev>2022-02-18 21:10:06 +0100
committerGitHub <noreply@github.com>2022-02-18 21:10:06 +0100
commit821e7cbc5a9711173061a272dc0378fff1fe5596 (patch)
treed7806a3d88dbf70d27996d9f85f66e93135670b2 /zellij-server/src/ui
parent10a22c479ffb4d76627eadbee2dc4b53ae4309c3 (diff)
feat(ui): add floating panes (#1066)
* basic functionality * close and reopen scratch terminal working * embed/float and resize whole tab for floating and static floating panes * move focus working * fix focus change in floating panes * move pane with mouse * floating z indices * tests and better resize algorithm * starting to work on performance * some performance experimentations * new render engine * reverse painters algorithm for floating panes * fix frame buffering * improve ux situation * handle multiple new panes on screen without overlap * adjust keybindings * adjust key hints * fix multiuser frame ui * fix various floating/multiuser bugs * remove stuff * wide characters under floating panes * fix wide character frame override * fix non-frame boundaries interactions with floating panes * fix selection character width * fix title frame wide char overflow * fix existing tests * add tests * refactor output out of tab * refactor floating panes out of tab * refactor tab * moar refactoring * refactorings and bring back terminal window title setting * add frame vte output * remove more unused stuff * remove even more unused stuff * you know the drill * refactor floating panes and remove more stuffs * refactor pane grids * remove unused output caching * refactor output * remove unused stuff * rustfmt * some formatting * rustfmt * reduce clippy to normal * remove comment * remove unused * fix closign pane * fix tests
Diffstat (limited to 'zellij-server/src/ui')
-rw-r--r--zellij-server/src/ui/boundaries.rs37
-rw-r--r--zellij-server/src/ui/pane_boundaries_frame.rs506
-rw-r--r--zellij-server/src/ui/pane_contents_and_ui.rs79
3 files changed, 345 insertions, 277 deletions
diff --git a/zellij-server/src/ui/boundaries.rs b/zellij-server/src/ui/boundaries.rs
index d26db14ac..649437415 100644
--- a/zellij-server/src/ui/boundaries.rs
+++ b/zellij-server/src/ui/boundaries.rs
@@ -1,9 +1,10 @@
use zellij_utils::{pane_size::Viewport, zellij_tile};
+use crate::output::CharacterChunk;
+use crate::panes::terminal_character::{TerminalCharacter, EMPTY_TERMINAL_CHARACTER, RESET_STYLES};
use crate::tab::Pane;
use ansi_term::Colour::{Fixed, RGB};
use std::collections::HashMap;
-use std::fmt::Write;
use zellij_tile::data::PaletteColor;
use zellij_utils::shared::colors;
@@ -43,6 +44,19 @@ impl BoundarySymbol {
self.color = color;
*self
}
+ pub fn as_terminal_character(&self) -> TerminalCharacter {
+ if self.invisible {
+ EMPTY_TERMINAL_CHARACTER
+ } else {
+ let character = self.boundary_type.chars().next().unwrap();
+ TerminalCharacter {
+ character,
+ width: 1,
+ styles: RESET_STYLES
+ .foreground(self.color.map(|palette_color| palette_color.into())),
+ }
+ }
+ }
}
impl Display for BoundarySymbol {
@@ -511,19 +525,16 @@ impl Boundaries {
}
}
}
- pub fn vte_output(&self) -> String {
- let mut vte_output = String::new();
+ pub fn render(&self) -> Vec<CharacterChunk> {
+ let mut character_chunks = vec![];
for (coordinates, boundary_character) in &self.boundary_characters {
- write!(
- &mut vte_output,
- "\u{1b}[{};{}H\u{1b}[m{}",
- coordinates.y + 1,
- coordinates.x + 1,
- boundary_character
- )
- .unwrap(); // goto row/col + boundary character
- }
- vte_output
+ character_chunks.push(CharacterChunk::new(
+ vec![boundary_character.as_terminal_character()],
+ coordinates.x,
+ coordinates.y,
+ ));
+ }
+ character_chunks
}
fn rect_right_boundary_is_before_screen_edge(&self, rect: &dyn Pane) -> bool {
rect.x() + rect.cols() < self.viewport.cols
diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs
index f2052412b..c46e55342 100644
--- a/zellij-server/src/ui/pane_boundaries_frame.rs
+++ b/zellij-server/src/ui/pane_boundaries_frame.rs
@@ -1,32 +1,64 @@
+use crate::output::CharacterChunk;
+use crate::panes::{AnsiCode, CharacterStyles, TerminalCharacter, EMPTY_TERMINAL_CHARACTER};
use crate::ui::boundaries::boundary_type;
use crate::ClientId;
-use ansi_term::Colour::{Fixed, RGB};
-use ansi_term::Style;
use zellij_utils::zellij_tile::prelude::{client_id_to_colors, Palette, PaletteColor};
use zellij_utils::{envs::get_session_name, pane_size::Viewport};
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
-use std::fmt::Write;
-
-fn color_string(character: &str, color: Option<PaletteColor>) -> String {
- match color {
- Some(PaletteColor::Rgb((r, g, b))) => RGB(r, g, b).bold().paint(character).to_string(),
- Some(PaletteColor::EightBit(color)) => Fixed(color).bold().paint(character).to_string(),
- None => Style::new().bold().paint(character).to_string(),
+fn foreground_color(characters: &str, color: Option<PaletteColor>) -> Vec<TerminalCharacter> {
+ let mut colored_string = Vec::with_capacity(characters.chars().count());
+ for character in characters.chars() {
+ let styles = match color {
+ Some(palette_color) => {
+ let mut styles = CharacterStyles::new();
+ styles.reset_all();
+ styles
+ .foreground(Some(AnsiCode::from(palette_color)))
+ .bold(Some(AnsiCode::On))
+ }
+ None => {
+ let mut styles = CharacterStyles::new();
+ styles.reset_all();
+ styles.bold(Some(AnsiCode::On))
+ }
+ };
+ let terminal_character = TerminalCharacter {
+ character,
+ styles,
+ width: character.width().unwrap_or(0),
+ };
+ colored_string.push(terminal_character);
}
+ colored_string
}
-fn background_color(character: &str, color: Option<PaletteColor>) -> String {
- match color {
- Some(PaletteColor::Rgb((r, g, b))) => {
- Style::new().on(RGB(r, g, b)).paint(character).to_string()
- }
- Some(PaletteColor::EightBit(color)) => {
- Style::new().on(Fixed(color)).paint(character).to_string()
- }
- None => character.to_string(),
+fn background_color(characters: &str, color: Option<PaletteColor>) -> Vec<TerminalCharacter> {
+ let mut colored_string = Vec::with_capacity(characters.chars().count());
+ for character in characters.chars() {
+ let styles = match color {
+ Some(palette_color) => {
+ let mut styles = CharacterStyles::new();
+ styles.reset_all();
+ styles
+ .background(Some(AnsiCode::from(palette_color)))
+ .bold(Some(AnsiCode::On))
+ }
+ None => {
+ let mut styles = CharacterStyles::new();
+ styles.reset_all();
+ styles
+ }
+ };
+ let terminal_character = TerminalCharacter {
+ character,
+ styles,
+ width: character.width().unwrap_or(0),
+ };
+ colored_string.push(terminal_character);
}
+ colored_string
}
pub struct FrameParams {
@@ -70,11 +102,14 @@ impl PaneFrame {
other_cursors_exist_in_session: frame_params.other_cursors_exist_in_session,
}
}
- fn client_cursor(&self, client_id: ClientId) -> String {
+ fn client_cursor(&self, client_id: ClientId) -> Vec<TerminalCharacter> {
let color = client_id_to_colors(client_id, self.colors);
background_color(" ", color.map(|c| c.0))
}
- fn render_title_right_side(&self, max_length: usize) -> Option<(String, usize)> {
+ fn render_title_right_side(
+ &self,
+ max_length: usize,
+ ) -> Option<(Vec<TerminalCharacter>, usize)> {
// string and length because of color
if self.scroll_position.0 > 0 || self.scroll_position.1 > 0 {
let prefix = " SCROLL: ";
@@ -86,17 +121,17 @@ impl PaneFrame {
let prefix_len = prefix.chars().count();
if prefix_len + full_indication_len <= max_length {
Some((
- color_string(&format!("{}{}", prefix, full_indication), self.color),
+ foreground_color(&format!("{}{}", prefix, full_indication), self.color),
prefix_len + full_indication_len,
))
} else if full_indication_len <= max_length {
Some((
- color_string(&full_indication, self.color),
+ foreground_color(&full_indication, self.color),
full_indication_len,
))
} else if short_indication_len <= max_length {
Some((
- color_string(&short_indication, self.color),
+ foreground_color(&short_indication, self.color),
short_indication_len,
))
} else {
@@ -106,24 +141,24 @@ impl PaneFrame {
None
}
}
- fn render_my_focus(&self, max_length: usize) -> Option<(String, usize)> {
- let left_separator = color_string(boundary_type::VERTICAL_LEFT, self.color);
- let right_separator = color_string(boundary_type::VERTICAL_RIGHT, self.color);
+ fn render_my_focus(&self, max_length: usize) -> Option<(Vec<TerminalCharacter>, usize)> {
+ let left_separator = foreground_color(boundary_type::VERTICAL_LEFT, self.color);
+ let right_separator = foreground_color(boundary_type::VERTICAL_RIGHT, self.color);
let full_indication_text = "MY FOCUS";
- let full_indication = format!(
- "{} {} {}",
- left_separator,
- color_string(full_indication_text, self.color),
- right_separator
- );
+ let mut full_indication = vec![];
+ full_indication.append(&mut left_separator.clone());
+ full_indication.push(EMPTY_TERMINAL_CHARACTER);
+ full_indication.append(&mut foreground_color(full_indication_text, self.color));
+ full_indication.push(EMPTY_TERMINAL_CHARACTER);
+ full_indication.append(&mut right_separator.clone());
let full_indication_len = full_indication_text.width() + 4; // 2 for separators 2 for padding
let short_indication_text = "ME";
- let short_indication = format!(
- "{} {} {}",
- left_separator,
- color_string(short_indication_text, self.color),
- right_separator
- );
+ let mut short_indication = vec![];
+ short_indication.append(&mut left_separator.clone());
+ short_indication.push(EMPTY_TERMINAL_CHARACTER);
+ short_indication.append(&mut foreground_color(short_indication_text, self.color));
+ short_indication.push(EMPTY_TERMINAL_CHARACTER);
+ short_indication.append(&mut right_separator.clone());
let short_indication_len = short_indication_text.width() + 4; // 2 for separators 2 for padding
if full_indication_len <= max_length {
Some((full_indication, full_indication_len))
@@ -133,91 +168,110 @@ impl PaneFrame {
None
}
}
- fn render_my_and_others_focus(&self, max_length: usize) -> Option<(String, usize)> {
- let left_separator = color_string(boundary_type::VERTICAL_LEFT, self.color);
- let right_separator = color_string(boundary_type::VERTICAL_RIGHT, self.color);
+ fn render_my_and_others_focus(
+ &self,
+ max_length: usize,
+ ) -> Option<(Vec<TerminalCharacter>, usize)> {
+ let mut left_separator = foreground_color(boundary_type::VERTICAL_LEFT, self.color);
+ let mut right_separator = foreground_color(boundary_type::VERTICAL_RIGHT, self.color);
let full_indication_text = "MY FOCUS AND:";
let short_indication_text = "+";
- let mut full_indication = color_string(full_indication_text, self.color);
+ let mut full_indication = foreground_color(full_indication_text, self.color);
let mut full_indication_len = full_indication_text.width();
- let mut short_indication = color_string(short_indication_text, self.color);
+ let mut short_indication = foreground_color(short_indication_text, self.color);
let mut short_indication_len = short_indication_text.width();
for client_id in &self.other_focused_clients {
- let text = format!(" {}", self.client_cursor(*client_id));
+ let mut text = self.client_cursor(*client_id);
full_indication_len += 2;
- full_indication.push_str(&text);
+ full_indication.push(EMPTY_TERMINAL_CHARACTER);
+ full_indication.append(&mut text.clone());
short_indication_len += 2;
- short_indication.push_str(&text);
+ short_indication.append(&mut text);
}
if full_indication_len + 4 <= max_length {
// 2 for separators, 2 for padding
- Some((
- format!("{} {} {}", left_separator, full_indication, right_separator),
- full_indication_len + 4,
- ))
+ let mut ret = vec![];
+ ret.append(&mut left_separator);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut full_indication);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut right_separator);
+ Some((ret, full_indication_len + 4))
} else if short_indication_len + 4 <= max_length {
// 2 for separators, 2 for padding
- Some((
- format!(
- "{} {} {}",
- left_separator, short_indication, right_separator
- ),
- short_indication_len + 4,
- ))
+ let mut ret = vec![];
+ ret.append(&mut left_separator);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut short_indication);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut right_separator);
+ Some((ret, short_indication_len + 4))
} else {
None
}
}
- fn render_other_focused_users(&self, max_length: usize) -> Option<(String, usize)> {
- let left_separator = color_string(boundary_type::VERTICAL_LEFT, self.color);
- let right_separator = color_string(boundary_type::VERTICAL_RIGHT, self.color);
+ fn render_other_focused_users(
+ &self,
+ max_length: usize,
+ ) -> Option<(Vec<TerminalCharacter>, usize)> {
+ let mut left_separator = foreground_color(boundary_type::VERTICAL_LEFT, self.color);
+ let mut right_separator = foreground_color(boundary_type::VERTICAL_RIGHT, self.color);
let full_indication_text = if self.other_focused_clients.len() == 1 {
"FOCUSED USER:"
} else {
"FOCUSED USERS:"
};
let middle_indication_text = "U:";
- let mut full_indication = color_string(full_indication_text, self.color);
+ let mut full_indication = foreground_color(full_indication_text, self.color);
let mut full_indication_len = full_indication_text.width();
- let mut middle_indication = color_string(middle_indication_text, self.color);
+ let mut middle_indication = foreground_color(middle_indication_text, self.color);
let mut middle_indication_len = middle_indication_text.width();
- let mut short_indication = String::from("");
+ let mut short_indication = vec![];
let mut short_indication_len = 0;
for client_id in &self.other_focused_clients {
- let text = format!(" {}", self.client_cursor(*client_id));
+ let mut text = self.client_cursor(*client_id);
full_indication_len += 2;
- full_indication.push_str(&text);
+ full_indication.push(EMPTY_TERMINAL_CHARACTER);
+ full_indication.append(&mut text.clone());
middle_indication_len += 2;
- middle_indication.push_str(&text);
+ middle_indication.push(EMPTY_TERMINAL_CHARACTER);
+ middle_indication.append(&mut text.clone());
short_indication_len += 2;
- short_indication.push_str(&text);
+ short_indication.push(EMPTY_TERMINAL_CHARACTER);
+ short_indication.append(&mut text);
}
if full_indication_len + 4 <= max_length {
// 2 for separators, 2 for padding
- Some((
- format!("{} {} {}", left_separator, full_indication, right_separator),
- full_indication_len + 4,
- ))
+ let mut ret = vec![];
+ ret.append(&mut left_separator);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut full_indication);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut right_separator);
+ Some((ret, full_indication_len + 4))
} else if middle_indication_len + 4 <= max_length {
// 2 for separators, 2 for padding
- Some((
- format!(
- "{} {} {}",
- left_separator, middle_indication, right_separator
- ),
- middle_indication_len + 4,
- ))
+ let mut ret = vec![];
+ ret.append(&mut left_separator);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut middle_indication);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut right_separator);
+ Some((ret, middle_indication_len + 4))
} else if short_indication_len + 3 <= max_length {
// 2 for separators, 1 for padding
- Some((
- format!("{}{} {}", left_separator, short_indication, right_separator),
- short_indication_len + 3,
- ))
+ let mut ret = vec![];
+ ret.append(&mut left_separator);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut short_indication);
+ ret.push(EMPTY_TERMINAL_CHARACTER);
+ ret.append(&mut right_separator);
+ Some((ret, short_indication_len + 3))
} else {
None
}
}
- fn render_title_middle(&self, max_length: usize) -> Option<(String, usize)> {
+ fn render_title_middle(&self, max_length: usize) -> Option<(Vec<TerminalCharacter>, usize)> {
// string and length because of color
if self.is_main_client
&& self.other_focused_clients.is_empty()
@@ -237,7 +291,7 @@ impl PaneFrame {
None
}
}
- fn render_title_left_side(&self, max_length: usize) -> Option<(String, usize)> {
+ fn render_title_left_side(&self, max_length: usize) -> Option<(Vec<TerminalCharacter>, usize)> {
let middle_truncated_sign = "[..]";
let middle_truncated_sign_long = "[...]";
let full_text = format!(" {} ", &self.title);
@@ -245,7 +299,7 @@ impl PaneFrame {
None
} else if full_text.width() <= max_length {
Some((
- color_string(&full_text, self.color),
+ foreground_color(&full_text, self.color),
full_text.chars().count(),
))
} else {
@@ -269,35 +323,39 @@ impl PaneFrame {
}
}
- let title_left_side =
- if first_part.width() + middle_truncated_sign.width() + second_part.width()
- < max_length
- {
- // this means we lost 1 character when dividing the total length into halves
+ let (title_left_side, title_length) = if first_part.width()
+ + middle_truncated_sign.width()
+ + second_part.width()
+ < max_length
+ {
+ // this means we lost 1 character when dividing the total length into halves
+ (
format!(
"{}{}{}",
first_part, middle_truncated_sign_long, second_part
- )
- } else {
- format!("{}{}{}", first_part, middle_truncated_sign, second_part)
- };
- Some((
- color_string(&title_left_side, self.color),
- title_left_side.chars().count(),
- ))
+ ),
+ first_part.width() + middle_truncated_sign_long.width() + second_part.width(),
+ )
+ } else {
+ (
+ format!("{}{}{}", first_part, middle_truncated_sign, second_part),
+ first_part.width() + middle_truncated_sign.width() + second_part.width(),
+ )
+ };
+ Some((foreground_color(&title_left_side, self.color), title_length))
}
}
fn three_part_title_line(
&self,
- left_side: &str,
+ mut left_side: Vec<TerminalCharacter>,
left_side_len: &usize,
- middle: &str,
+ mut middle: Vec<TerminalCharacter>,
middle_len: &usize,
- right_side: &str,
+ mut right_side: Vec<TerminalCharacter>,
right_side_len: &usize,
- ) -> String {
+ ) -> Vec<TerminalCharacter> {
let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners
- let mut title_line = String::new();
+ let mut title_line = vec![];
let left_side_start_position = self.geom.x + 1;
let middle_start_position = self.geom.x + (total_title_length / 2) - (middle_len / 2) + 1;
let right_side_start_position =
@@ -306,24 +364,23 @@ impl PaneFrame {
let mut col = self.geom.x;
loop {
if col == self.geom.x {
- title_line.push_str(&color_string(boundary_type::TOP_LEFT, self.color));
+ title_line.append(&mut foreground_color(boundary_type::TOP_LEFT, self.color));
} else if col == self.geom.x + self.geom.cols - 1 {
- title_line.push_str(&color_string(boundary_type::TOP_RIGHT, self.color));
+ title_line.append(&mut foreground_color(boundary_type::TOP_RIGHT, self.color));
} else if col == left_side_start_position {
- title_line.push_str(left_side);
+ title_line.append(&mut left_side);
col += left_side_len;
continue;
} else if col == middle_start_position {
- title_line.push_str(middle);
+ title_line.append(&mut middle);
col += middle_len;
continue;
} else if col == right_side_start_position {
- title_line.push_str(right_side);
+ title_line.append(&mut right_side);
col += right_side_len;
continue;
} else {
- title_line.push_str(&color_string(boundary_type::HORIZONTAL, self.color));
- // TODO: BETTER
+ title_line.append(&mut foreground_color(boundary_type::HORIZONTAL, self.color));
}
if col == self.geom.x + self.geom.cols - 1 {
break;
@@ -334,33 +391,32 @@ impl PaneFrame {
}
fn left_and_middle_title_line(
&self,
- left_side: &str,
+ mut left_side: Vec<TerminalCharacter>,
left_side_len: &usize,
- middle: &str,
+ mut middle: Vec<TerminalCharacter>,
middle_len: &usize,
- ) -> String {
+ ) -> Vec<TerminalCharacter> {
let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners
- let mut title_line = String::new();
+ let mut title_line = vec![];
let left_side_start_position = self.geom.x + 1;
let middle_start_position = self.geom.x + (total_title_length / 2) - (*middle_len / 2) + 1;
let mut col = self.geom.x;
loop {
if col == self.geom.x {
- title_line.push_str(&color_string(boundary_type::TOP_LEFT, self.color));
+ title_line.append(&mut foreground_color(boundary_type::TOP_LEFT, self.color));
} else if col == self.geom.x + self.geom.cols - 1 {
- title_line.push_str(&color_string(boundary_type::TOP_RIGHT, self.color));
+ title_line.append(&mut foreground_color(boundary_type::TOP_RIGHT, self.color));
} else if col == left_side_start_position {
- title_line.push_str(left_side);
+ title_line.append(&mut left_side);
col += *left_side_len;
continue;
} else if col == middle_start_position {
- title_line.push_str(middle);
+ title_line.append(&mut middle);
col += *middle_len;
continue;
} else {
- title_line.push_str(&color_string(boundary_type::HORIZONTAL, self.color));
- // TODO: BETTER
+ title_line.append(&mut foreground_color(boundary_type::HORIZONTAL, self.color));
}
if col == self.geom.x + self.geom.cols - 1 {
break;
@@ -369,24 +425,27 @@ impl PaneFrame {
}
title_line
}
- fn middle_only_title_line(&self, middle: &str, middle_len: &usize) -> String {
+ fn middle_only_title_line(
+ &self,
+ mut middle: Vec<TerminalCharacter>,
+ middle_len: &usize,
+ ) -> Vec<TerminalCharacter> {
let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners
- let mut title_line = String::new();
+ let mut title_line = vec![];
let middle_start_position = self.geom.x + (total_title_length / 2) - (*middle_len / 2) + 1;
let mut col = self.geom.x;
loop {
if col == self.geom.x {
- title_line.push_str(&color_string(boundary_type::TOP_LEFT, self.color));
+ title_line.append(&mut foreground_color(boundary_type::TOP_LEFT, self.color));
} else if col == self.geom.x + self.geom.cols - 1 {
- title_line.push_str(&color_string(boundary_type::TOP_RIGHT, self.color));
+ title_line.append(&mut foreground_color(boundary_type::TOP_RIGHT, self.color));
} else if col == middle_start_position {
- title_line.push_str(middle);
+ title_line.append(&mut middle);
col += *middle_len;
continue;
} else {
- title_line.push_str(&color_string(boundary_type::HORIZONTAL, self.color));
- // TODO: BETTER
+ title_line.append(&mut foreground_color(boundary_type::HORIZONTAL, self.color));
}
if col == self.geom.x + self.geom.cols - 1 {
break;
@@ -397,81 +456,86 @@ impl PaneFrame {
}
fn two_part_title_line(
&self,
- left_side: &str,
+ mut left_side: Vec<TerminalCharacter>,
left_side_len: &usize,
- right_side: &str,
+ mut right_side: Vec<TerminalCharacter>,
right_side_len: &usize,
- ) -> String {
- let left_boundary = color_string(boundary_type::TOP_LEFT, self.color);
- let right_boundary = color_string(boundary_type::TOP_RIGHT, self.color);
+ ) -> Vec<TerminalCharacter> {
+ let mut left_boundary = foreground_color(boundary_type::TOP_LEFT, self.color);
+ let mut right_boundary = foreground_color(boundary_type::TOP_RIGHT, self.color);
let total_title_length = self.geom.cols.saturating_sub(2); // 2 for the left and right corners
let mut middle = String::new();
for _ in (left_side_len + right_side_len)..total_title_length {
middle.push_str(boundary_type::HORIZONTAL);
}
- format!(
- "{}{}{}{}{}",
- left_boundary,
- left_side,
- color_string(&middle, self.color),
- color_string(right_side, self.color),
- &right_boundary
- )
+ let mut ret = vec![];
+ ret