diff --git a/CHANGELOG.md b/CHANGELOG.md index 7908d67e..8cf07418 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) * Fix bug when opening new tab the new pane's viewport would sometimes be calculated incorrectly (https://github.com/zellij-org/zellij/pull/683) * Fix bug when in some cases closing a tab would not clear the previous pane's contents (https://github.com/zellij-org/zellij/pull/684) * Fix bug where tabs would sometimes be created with the wrong index in their name (https://github.com/zellij-org/zellij/pull/686) +* Fix bug where wide chars would mess up pane titles (https://github.com/zellij-org/zellij/pull/698) ## [0.16.0] - 2021-08-31 * Plugins don't crash zellij anymore on receiving mouse events (https://github.com/zellij-org/zellij/pull/620) diff --git a/test-template.yaml b/test-template.yaml new file mode 100644 index 00000000..b9c496d4 --- /dev/null +++ b/test-template.yaml @@ -0,0 +1,20 @@ +--- +template: + direction: Horizontal + parts: + - direction: Vertical + borderless: true + split_size: + Fixed: 1 + run: + plugin: tab-bar + - direction: Vertical + borderless: true + - direction: Vertical + borderless: true + - direction: Vertical + borderless: true + split_size: + Fixed: 2 + run: + plugin: status-bar diff --git a/zellij-server/src/ui/pane_boundaries_frame.rs b/zellij-server/src/ui/pane_boundaries_frame.rs index d3cd6d5a..49344149 100644 --- a/zellij-server/src/ui/pane_boundaries_frame.rs +++ b/zellij-server/src/ui/pane_boundaries_frame.rs @@ -4,6 +4,8 @@ use ansi_term::Style; use zellij_utils::pane_size::Viewport; use zellij_utils::zellij_tile::prelude::PaletteColor; +use unicode_width::{UnicodeWidthChar, UnicodeWidthStr}; + fn color_string(character: &str, color: Option) -> String { match color { Some(color) => match color { @@ -33,11 +35,11 @@ impl PaneFrame { let full_indication = format!(" {}/{} ", self.scroll_position.0, self.scroll_position.1); let short_indication = format!(" {} ", self.scroll_position.0); - if prefix.chars().count() + full_indication.chars().count() <= max_length { + if prefix.width() + full_indication.width() <= max_length { Some(format!("{}{}", prefix, full_indication)) - } else if full_indication.chars().count() <= max_length { + } else if full_indication.width() <= max_length { Some(full_indication) - } else if short_indication.chars().count() <= max_length { + } else if short_indication.width() <= max_length { Some(short_indication) } else { None @@ -52,28 +54,41 @@ impl PaneFrame { let full_text = format!(" {} ", &self.title); if max_length <= 6 { None - } else if full_text.chars().count() <= max_length { + } else if full_text.width() <= max_length { Some(full_text) } else { - let length_of_each_half = (max_length - middle_truncated_sign.chars().count()) / 2; - let first_part: String = full_text.chars().take(length_of_each_half).collect(); - let second_part: String = full_text - .chars() - .skip(full_text.chars().count() - length_of_each_half) - .collect(); - let title_left_side = if first_part.chars().count() - + middle_truncated_sign.chars().count() - + second_part.chars().count() - < 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) - }; + let length_of_each_half = (max_length - middle_truncated_sign.width()) / 2; + + let mut first_part: String = String::with_capacity(length_of_each_half); + for char in full_text.chars() { + if first_part.width() + char.width().unwrap_or(0) > length_of_each_half { + break; + } else { + first_part.push(char); + } + } + + let mut second_part: String = String::with_capacity(length_of_each_half); + for char in full_text.chars().rev() { + if second_part.width() + char.width().unwrap_or(0) > length_of_each_half { + break; + } else { + second_part.insert(0, char); + } + } + + 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 + format!( + "{}{}{}", + first_part, middle_truncated_sign_long, second_part + ) + } else { + format!("{}{}{}", first_part, middle_truncated_sign, second_part) + }; Some(title_left_side) } } @@ -83,15 +98,13 @@ impl PaneFrame { let right_boundary = boundary_type::TOP_RIGHT; let left_side = self.render_title_left_side(total_title_length); let right_side = left_side.as_ref().and_then(|left_side| { - let space_left = total_title_length.saturating_sub(left_side.chars().count() + 1); // 1 for a middle separator + let space_left = total_title_length.saturating_sub(left_side.width() + 1); // 1 for a middle separator self.render_title_right_side(space_left) }); let title_text = match (left_side, right_side) { (Some(left_side), Some(right_side)) => { let mut middle = String::new(); - for _ in - (left_side.chars().count() + right_side.chars().count())..total_title_length - { + for _ in (left_side.width() + right_side.width())..total_title_length { middle.push_str(boundary_type::HORIZONTAL); } format!( @@ -101,7 +114,7 @@ impl PaneFrame { } (Some(left_side), None) => { let mut middle_padding = String::new(); - for _ in left_side.chars().count()..total_title_length { + for _ in left_side.width()..total_title_length { middle_padding.push_str(boundary_type::HORIZONTAL); } format!(