widgets: allow text overflow in text fields
Show text content of a text field that exceeds the visible width properly.jmap-eventsource
parent
f69f623818
commit
d4f508642a
|
@ -33,6 +33,8 @@ pub use wcwidth::*;
|
||||||
pub trait Truncate {
|
pub trait Truncate {
|
||||||
fn truncate_at_boundary(&mut self, new_len: usize);
|
fn truncate_at_boundary(&mut self, new_len: usize);
|
||||||
fn trim_at_boundary(&self, new_len: usize) -> &str;
|
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 {
|
impl Truncate for &str {
|
||||||
|
@ -67,6 +69,39 @@ impl Truncate for &str {
|
||||||
self
|
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 {
|
impl Truncate for String {
|
||||||
|
@ -101,6 +136,39 @@ impl Truncate for String {
|
||||||
self.as_str()
|
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 {
|
pub trait GlobMatch {
|
||||||
|
|
|
@ -111,12 +111,15 @@ impl Field {
|
||||||
let upper_left = upper_left!(area);
|
let upper_left = upper_left!(area);
|
||||||
match self {
|
match self {
|
||||||
Text(ref term, auto_complete_fn) => {
|
Text(ref term, auto_complete_fn) => {
|
||||||
|
let width = width!(area);
|
||||||
|
let pos = if width < term.grapheme_pos() {
|
||||||
|
width
|
||||||
|
} else {
|
||||||
|
term.grapheme_pos()
|
||||||
|
};
|
||||||
change_colors(
|
change_colors(
|
||||||
grid,
|
grid,
|
||||||
(
|
(pos_inc(upper_left, (pos, 0)), pos_inc(upper_left, (pos, 0))),
|
||||||
pos_inc(upper_left, (term.grapheme_pos(), 0)),
|
|
||||||
(pos_inc(upper_left, (term.grapheme_pos(), 0))),
|
|
||||||
),
|
|
||||||
Color::Default,
|
Color::Default,
|
||||||
Color::Byte(248),
|
Color::Byte(248),
|
||||||
);
|
);
|
||||||
|
@ -137,15 +140,89 @@ impl Field {
|
||||||
impl Component for Field {
|
impl Component for Field {
|
||||||
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
fn draw(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
|
||||||
let theme_attr = crate::conf::value(context, "widgets.form.field");
|
let theme_attr = crate::conf::value(context, "widgets.form.field");
|
||||||
write_string_to_grid(
|
let width = width!(area);
|
||||||
self.as_str(),
|
let str = self.as_str();
|
||||||
grid,
|
match self {
|
||||||
theme_attr.fg,
|
Text(ref term, _) => {
|
||||||
theme_attr.bg,
|
/* Calculate which part of the str is visible
|
||||||
theme_attr.attrs,
|
* ##########################################
|
||||||
area,
|
*
|
||||||
None,
|
* 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 {
|
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
|
||||||
match *event {
|
match *event {
|
||||||
|
@ -496,10 +573,7 @@ impl<T: 'static + std::fmt::Debug + Copy + Default + Send + Sync> Component for
|
||||||
grid,
|
grid,
|
||||||
(
|
(
|
||||||
pos_inc(upper_left, (self.field_name_max_length + 3, i)),
|
pos_inc(upper_left, (self.field_name_max_length + 3, i)),
|
||||||
(
|
(get_x(bottom_right), i + get_y(upper_left)),
|
||||||
get_x(upper_left) + self.field_name_max_length + 3,
|
|
||||||
i + get_y(upper_left),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
pos_inc(upper_left, (self.field_name_max_length + 3, i + 1)),
|
pos_inc(upper_left, (self.field_name_max_length + 3, i + 1)),
|
||||||
|
|
Loading…
Reference in New Issue