widgets: allow text overflow in text fields

Show text content of a text field that exceeds the visible width
properly.
jmap-eventsource
Manos Pitsidianakis 2020-11-24 09:31:38 +02:00
parent f69f623818
commit d4f508642a
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
2 changed files with 159 additions and 17 deletions

View File

@ -33,6 +33,8 @@ pub use wcwidth::*;
pub trait Truncate {
fn truncate_at_boundary(&mut self, new_len: usize);
fn trim_at_boundary(&self, new_len: usize) -> &str;
fn trim_left_at_boundary(&self, new_len: usize) -> &str;
fn truncate_left_at_boundary(&mut self, new_len: usize);
}
impl Truncate for &str {
@ -67,6 +69,39 @@ impl Truncate for &str {
self
}
}
fn trim_left_at_boundary(&self, skip_len: usize) -> &str {
if skip_len >= self.len() {
return "";
}
extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;
if let Some((first, _)) = UnicodeSegmentation::grapheme_indices(*self, true)
.skip(skip_len)
.next()
{
&self[first..]
} else {
self
}
}
fn truncate_left_at_boundary(&mut self, skip_len: usize) {
if skip_len >= self.len() {
*self = "";
return;
}
extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;
if let Some((first, _)) = UnicodeSegmentation::grapheme_indices(*self, true)
.skip(skip_len)
.next()
{
*self = &self[first..];
}
}
}
impl Truncate for String {
@ -101,6 +136,39 @@ impl Truncate for String {
self.as_str()
}
}
fn trim_left_at_boundary(&self, skip_len: usize) -> &str {
if skip_len >= self.len() {
return "";
}
extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;
if let Some((first, _)) = UnicodeSegmentation::grapheme_indices(self.as_str(), true)
.skip(skip_len)
.next()
{
&self[first..]
} else {
self.as_str()
}
}
fn truncate_left_at_boundary(&mut self, skip_len: usize) {
if skip_len >= self.len() {
self.clear();
return;
}
extern crate unicode_segmentation;
use unicode_segmentation::UnicodeSegmentation;
if let Some((first, _)) = UnicodeSegmentation::grapheme_indices(self.as_str(), true)
.skip(skip_len)
.next()
{
*self = self[first..].to_string();
}
}
}
pub trait GlobMatch {

View File

@ -111,12 +111,15 @@ impl Field {
let upper_left = upper_left!(area);
match self {
Text(ref term, auto_complete_fn) => {
let width = width!(area);
let pos = if width < term.grapheme_pos() {
width
} else {
term.grapheme_pos()
};
change_colors(
grid,
(
pos_inc(upper_left, (term.grapheme_pos(), 0)),
(pos_inc(upper_left, (term.grapheme_pos(), 0))),
),
(pos_inc(upper_left, (pos, 0)), pos_inc(upper_left, (pos, 0))),
Color::Default,
Color::Byte(248),
);
@ -137,15 +140,89 @@ impl Field {
impl Component for Field {
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
let theme_attr = crate::conf::value(context, "widgets.form.field");
write_string_to_grid(
self.as_str(),
grid,
theme_attr.fg,
theme_attr.bg,
theme_attr.attrs,
area,
None,
);
let width = width!(area);
let str = self.as_str();
match self {
Text(ref term, _) => {
/* Calculate which part of the str is visible
* ##########################################
*
* Example:
* For the string "The quick brown fox jumps over the lazy dog" with visible width
* of field of 10 columns
*
*
* Cursor <= width
* =================
* Cursor at:
* โ‡ฉ
* The quick brown fox jumps over the lazy dog
*
* cursor
* โ‡ฉ
* โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
* โ”‚The quick โ”‚ brown fox jumps over the lazy dog
* โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
*
* No skip.
*
* Cursor at the end
* =================
* Cursor at:
* โ‡ฉ
* The quick brown fox jumps over the lazy dog
*
* remainder cursor
* โ‡ฉโ‡ฉโ‡ฉโ‡ฉโ‡ฉ โ‡ฉ
* +โ•Œโ•Œโ•Œ+โ•ญโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฎโ•ญโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฎโ•ญโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฎโ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
* |The|โ”Š quick broโ”Šโ”Šwn fox jumโ”Šโ”Šps over thโ”Šโ”‚e lazy dogโ”‚
* +โ•Œโ•Œโ•Œ+โ•ฐโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฏโ•ฐโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฏโ•ฐโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฏโ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
* โ‡งโ‡งโ‡ง++โ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡ง++โ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡ง++โ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡ง
* skip offset
*
* Intermediate cursor
* ===================
* Cursor at:
* โ‡ฉ
* The quick brown fox jumps over the lazy dog
*
* remainder cursor
* โ‡ฉ โ‡ฉ
* +โ•ญโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฎโ•ญโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฎโ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
* T|he quick bโ”Šโ”Šrown fox jโ”Šโ”‚umps over โ”‚ the lazy dog
* +โ•ฐโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฏโ•ฐโ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ”…โ•ฏโ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
* โ‡ง+โ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡ง++โ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡งโ‡ง
* skip offset
*/
write_string_to_grid(
if width < term.grapheme_pos() {
str.trim_left_at_boundary(
width * term.grapheme_pos().wrapping_div(width).saturating_sub(1)
+ term.grapheme_pos().wrapping_rem(width),
)
} else {
str
},
grid,
theme_attr.fg,
theme_attr.bg,
theme_attr.attrs,
area,
None,
);
}
Choice(_, _) => {
write_string_to_grid(
str,
grid,
theme_attr.fg,
theme_attr.bg,
theme_attr.attrs,
area,
None,
);
}
}
}
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
match *event {
@ -496,10 +573,7 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Component for
grid,
(
pos_inc(upper_left, (self.field_name_max_length + 3, i)),
(
get_x(upper_left) + self.field_name_max_length + 3,
i + get_y(upper_left),
),
(get_x(bottom_right), i + get_y(upper_left)),
),
(
pos_inc(upper_left, (self.field_name_max_length + 3, i + 1)),