fix(borders): properly handle wide chars in pane titles (#698)

* work

* fix(frame): properly handle widechars in frame titles

* style(fmt): make rustfmt happy

* fix(truncate): do not reverse second part of string (oops)

* docs(changelog): document change
This commit is contained in:
Aram Drevekenin 2021-09-06 20:24:47 +02:00 committed by GitHub
parent 1c9dc35121
commit 86fdd2400e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 28 deletions

View file

@ -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 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 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 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 ## [0.16.0] - 2021-08-31
* Plugins don't crash zellij anymore on receiving mouse events (https://github.com/zellij-org/zellij/pull/620) * Plugins don't crash zellij anymore on receiving mouse events (https://github.com/zellij-org/zellij/pull/620)

20
test-template.yaml Normal file
View file

@ -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

View file

@ -4,6 +4,8 @@ use ansi_term::Style;
use zellij_utils::pane_size::Viewport; use zellij_utils::pane_size::Viewport;
use zellij_utils::zellij_tile::prelude::PaletteColor; use zellij_utils::zellij_tile::prelude::PaletteColor;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
fn color_string(character: &str, color: Option<PaletteColor>) -> String { fn color_string(character: &str, color: Option<PaletteColor>) -> String {
match color { match color {
Some(color) => match color { Some(color) => match color {
@ -33,11 +35,11 @@ impl PaneFrame {
let full_indication = let full_indication =
format!(" {}/{} ", self.scroll_position.0, self.scroll_position.1); format!(" {}/{} ", self.scroll_position.0, self.scroll_position.1);
let short_indication = format!(" {} ", self.scroll_position.0); 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)) Some(format!("{}{}", prefix, full_indication))
} else if full_indication.chars().count() <= max_length { } else if full_indication.width() <= max_length {
Some(full_indication) Some(full_indication)
} else if short_indication.chars().count() <= max_length { } else if short_indication.width() <= max_length {
Some(short_indication) Some(short_indication)
} else { } else {
None None
@ -52,28 +54,41 @@ impl PaneFrame {
let full_text = format!(" {} ", &self.title); let full_text = format!(" {} ", &self.title);
if max_length <= 6 { if max_length <= 6 {
None None
} else if full_text.chars().count() <= max_length { } else if full_text.width() <= max_length {
Some(full_text) Some(full_text)
} else { } else {
let length_of_each_half = (max_length - middle_truncated_sign.chars().count()) / 2; let length_of_each_half = (max_length - middle_truncated_sign.width()) / 2;
let first_part: String = full_text.chars().take(length_of_each_half).collect();
let second_part: String = full_text let mut first_part: String = String::with_capacity(length_of_each_half);
.chars() for char in full_text.chars() {
.skip(full_text.chars().count() - length_of_each_half) if first_part.width() + char.width().unwrap_or(0) > length_of_each_half {
.collect(); break;
let title_left_side = if first_part.chars().count() } else {
+ middle_truncated_sign.chars().count() first_part.push(char);
+ second_part.chars().count() }
< max_length }
{
// this means we lost 1 character when dividing the total length into halves let mut second_part: String = String::with_capacity(length_of_each_half);
format!( for char in full_text.chars().rev() {
"{}{}{}", if second_part.width() + char.width().unwrap_or(0) > length_of_each_half {
first_part, middle_truncated_sign_long, second_part break;
) } else {
} else { second_part.insert(0, char);
format!("{}{}{}", first_part, middle_truncated_sign, second_part) }
}; }
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) Some(title_left_side)
} }
} }
@ -83,15 +98,13 @@ impl PaneFrame {
let right_boundary = boundary_type::TOP_RIGHT; let right_boundary = boundary_type::TOP_RIGHT;
let left_side = self.render_title_left_side(total_title_length); let left_side = self.render_title_left_side(total_title_length);
let right_side = left_side.as_ref().and_then(|left_side| { 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) self.render_title_right_side(space_left)
}); });
let title_text = match (left_side, right_side) { let title_text = match (left_side, right_side) {
(Some(left_side), Some(right_side)) => { (Some(left_side), Some(right_side)) => {
let mut middle = String::new(); let mut middle = String::new();
for _ in for _ in (left_side.width() + right_side.width())..total_title_length {
(left_side.chars().count() + right_side.chars().count())..total_title_length
{
middle.push_str(boundary_type::HORIZONTAL); middle.push_str(boundary_type::HORIZONTAL);
} }
format!( format!(
@ -101,7 +114,7 @@ impl PaneFrame {
} }
(Some(left_side), None) => { (Some(left_side), None) => {
let mut middle_padding = String::new(); 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); middle_padding.push_str(boundary_type::HORIZONTAL);
} }
format!( format!(