Browse Source

ui/themes: add NO_COLOR support

https://no-color.org/
async
Manos Pitsidianakis 2 years ago
parent
commit
6a7cae0988
Signed by untrusted user: epilys GPG Key ID: 73627C2F690DF710
  1. 4
      meli.1
  2. 92
      ui/src/components/mail/listing.rs
  3. 27
      ui/src/components/mail/listing/compact.rs
  4. 51
      ui/src/components/mail/listing/conversations.rs
  5. 77
      ui/src/components/mail/listing/plain.rs
  6. 289
      ui/src/components/utilities.rs
  7. 35
      ui/src/components/utilities/widgets.rs
  8. 3
      ui/src/conf/terminal.rs
  9. 66
      ui/src/conf/themes.rs
  10. 44
      ui/src/state.rs
  11. 48
      ui/src/terminal/cells.rs

4
meli.1

@ -402,6 +402,10 @@ catchall for general errors
Specifies the editor to use
.It Ev MELI_CONFIG
Override the configuration file
.It Ev NO_COLOR
When present (regardless of its value), prevents the addition of ANSI color. The configuration value
.Ic use_color
overrides this.
.El
.Sh FILES
.Nm

92
ui/src/components/mail/listing.rs

@ -1050,49 +1050,38 @@ impl Listing {
if idx == lines_len {
break;
}
let (
fg_color,
bg_color,
index_fg_color,
index_bg_color,
unread_count_fg,
unread_count_bg,
) = if must_highlight_account {
let (att, index_att, unread_count_att) = if must_highlight_account {
if self.cursor_pos.1 == idx {
(
crate::conf::value(context, "mail.sidebar_highlighted").fg,
crate::conf::value(context, "mail.sidebar_highlighted").bg,
crate::conf::value(context, "mail.sidebar_highlighted_index").fg,
crate::conf::value(context, "mail.sidebar_highlighted_index").bg,
crate::conf::value(context, "mail.sidebar_highlighted_unread_count").fg,
crate::conf::value(context, "mail.sidebar_highlighted_unread_count").bg,
)
let mut ret = (
crate::conf::value(context, "mail.sidebar_highlighted"),
crate::conf::value(context, "mail.sidebar_highlighted_index"),
crate::conf::value(context, "mail.sidebar_highlighted_unread_count"),
);
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
ret.0.attrs |= Attr::Reverse;
ret.1.attrs |= Attr::Reverse;
ret.2.attrs |= Attr::Reverse;
}
ret
} else {
(
crate::conf::value(context, "mail.sidebar_highlighted_account").fg,
crate::conf::value(context, "mail.sidebar_highlighted_account").bg,
crate::conf::value(context, "mail.sidebar_highlighted_account_index").fg,
crate::conf::value(context, "mail.sidebar_highlighted_account_index").bg,
crate::conf::value(context, "mail.sidebar_highlighted_account"),
crate::conf::value(context, "mail.sidebar_highlighted_account_index"),
crate::conf::value(
context,
"mail.sidebar_highlighted_account_unread_count",
)
.fg,
crate::conf::value(
context,
"mail.sidebar_highlighted_account_unread_count",
)
.bg,
),
)
}
} else {
(
crate::conf::value(context, "mail.sidebar").fg,
crate::conf::value(context, "mail.sidebar").bg,
crate::conf::value(context, "mail.sidebar_index").fg,
crate::conf::value(context, "mail.sidebar_index").bg,
crate::conf::value(context, "mail.sidebar_unread_count").fg,
crate::conf::value(context, "mail.sidebar_unread_count").bg,
crate::conf::value(context, "mail.sidebar"),
crate::conf::value(context, "mail.sidebar_index"),
crate::conf::value(context, "mail.sidebar_unread_count"),
)
};
@ -1117,27 +1106,27 @@ impl Listing {
let (x, _) = write_string_to_grid(
&format!("{:>width$}", inc, width = total_folder_no_digits),
grid,
index_fg_color,
index_bg_color,
Attr::Default,
index_att.fg,
index_att.bg,
index_att.attrs,
(set_y(upper_left, y), bottom_right),
None,
);
let (x, _) = write_string_to_grid(
&" ".repeat(depth + 1),
grid,
fg_color,
bg_color,
Attr::Default,
att.fg,
att.bg,
att.attrs,
((x, y), bottom_right),
None,
);
let (x, _) = write_string_to_grid(
entries[&folder_idx].name(),
grid,
fg_color,
bg_color,
Attr::Default,
att.fg,
att.bg,
att.attrs,
((x, y), bottom_right),
None,
);
@ -1156,13 +1145,14 @@ impl Listing {
let (x, _) = write_string_to_grid(
&count_string,
grid,
unread_count_fg,
unread_count_bg,
if count.unwrap_or(0) > 0 {
Attr::Bold
} else {
Attr::Default
},
unread_count_att.fg,
unread_count_att.bg,
unread_count_att.attrs
| if count.unwrap_or(0) > 0 {
Attr::Bold
} else {
Attr::Default
},
(
(
/* Hide part of folder name if need be to fit the message count */
@ -1173,7 +1163,9 @@ impl Listing {
),
None,
);
change_colors(grid, ((x, y), set_y(bottom_right, y)), fg_color, bg_color);
for c in grid.row_iter(x..(get_x(bottom_right) + 1), y) {
grid[c].set_fg(att.fg).set_bg(att.bg).set_attrs(att.attrs);
}
idx += 1;
}
if idx == 0 {

27
ui/src/components/mail/listing/compact.rs

@ -146,15 +146,32 @@ impl ListingTrait for CompactListing {
} else {
self.color_cache.odd.bg
};
let attrs = if self.cursor_pos.2 == idx {
self.color_cache.highlighted.attrs
} else if self.selection[&thread_hash] {
self.color_cache.selected.attrs
} else if thread.unseen() > 0 {
self.color_cache.unseen.attrs
} else if idx % 2 == 0 {
self.color_cache.even.attrs
} else {
self.color_cache.odd.attrs
};
let (upper_left, bottom_right) = area;
change_colors(grid, area, fg_color, bg_color);
let x = get_x(upper_left)
+ self.data_columns.widths[0]
+ self.data_columns.widths[1]
+ self.data_columns.widths[2]
+ 3 * 2;
for c in grid.row_iter(
get_x(upper_left)..(get_x(bottom_right) + 1),
get_y(upper_left),
) {
grid[c].set_fg(fg_color).set_bg(bg_color).set_attrs(attrs);
}
copy_area(
grid,
&self.data_columns.columns[3],
@ -165,7 +182,7 @@ impl ListingTrait for CompactListing {
),
);
for c in grid.row_iter(x..(self.data_columns.widths[3] + x), get_y(upper_left)) {
grid[c].set_bg(bg_color);
grid[c].set_bg(bg_color).set_attrs(attrs);
}
return;
}
@ -641,6 +658,12 @@ impl CompactListing {
thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
..self.color_cache
};
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
self.color_cache.highlighted.attrs |= Attr::Reverse;
}
// Get mailbox as a reference.
//

51
ui/src/components/mail/listing/conversations.rs

@ -119,6 +119,15 @@ impl ListingTrait for ConversationsListing {
} else {
self.color_cache.theme_default.bg
};
let attrs = if self.cursor_pos.2 == idx {
self.color_cache.highlighted.attrs
} else if self.selection[&thread_hash] {
self.color_cache.selected.attrs
} else if thread.unseen() > 0 {
self.color_cache.unseen.attrs
} else {
self.color_cache.theme_default.attrs
};
copy_area(
grid,
@ -134,26 +143,38 @@ impl ListingTrait for ConversationsListing {
let (x, y) = upper_left;
if self.cursor_pos.2 == idx || self.selection[&thread_hash] {
for x in x..=get_x(bottom_right) {
grid[(x, y)].set_fg(fg_color);
grid[(x, y)].set_bg(bg_color);
grid[(x, y)]
.set_fg(fg_color)
.set_bg(bg_color)
.set_attrs(attrs);
grid[(x, y + 1)].set_fg(fg_color);
grid[(x, y + 1)].set_bg(bg_color);
grid[(x, y + 1)]
.set_fg(fg_color)
.set_bg(bg_color)
.set_attrs(attrs);
grid[(x, y + 2)].set_fg(padding_fg);
grid[(x, y + 2)].set_bg(bg_color);
grid[(x, y + 2)]
.set_fg(padding_fg)
.set_bg(bg_color)
.set_attrs(attrs);
}
} else if width < width!(area) {
/* fill any remaining columns, if our view is wider than self.content */
for x in (x + width)..=get_x(bottom_right) {
grid[(x, y)].set_fg(fg_color);
grid[(x, y)].set_bg(bg_color);
grid[(x, y)]
.set_fg(fg_color)
.set_bg(bg_color)
.set_attrs(attrs);
grid[(x, y + 1)].set_fg(fg_color);
grid[(x, y + 1)].set_bg(bg_color);
grid[(x, y + 1)]
.set_fg(fg_color)
.set_bg(bg_color)
.set_attrs(attrs);
grid[(x, y + 2)].set_fg(padding_fg);
grid[(x, y + 2)].set_bg(bg_color);
grid[(x, y + 2)]
.set_fg(padding_fg)
.set_bg(bg_color)
.set_attrs(attrs);
}
}
return;
@ -570,6 +591,12 @@ impl ConversationsListing {
..self.color_cache
};
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
self.color_cache.highlighted.attrs |= Attr::Reverse;
}
// Get mailbox as a reference.
//
match context.accounts[self.cursor_pos.0].status(self.folder_hash) {

77
ui/src/components/mail/listing/plain.rs

@ -73,6 +73,7 @@ pub struct PlainListing {
view: MailView,
row_updates: SmallVec<[EnvelopeHash; 8]>,
_row_updates: SmallVec<[ThreadHash; 8]>,
color_cache: ColorCache,
movement: Option<PageMovement>,
id: ComponentId,
@ -125,37 +126,51 @@ impl ListingTrait for PlainListing {
let account = &context.accounts[self.cursor_pos.0];
let envelope: EnvelopeRef = account.collection.get_env(i);
let fg_color = self.data_columns.columns[0][(0, idx)].fg();
let bg_color = if context.settings.terminal.theme == "light" {
if self.cursor_pos.2 == idx {
Color::Byte(244)
} else if self.selection[&i] {
Color::Byte(210)
} else if !envelope.is_seen() {
Color::Byte(251)
} else {
self.data_columns.columns[0][(0, idx)].bg()
}
let fg_color = if !envelope.is_seen() {
self.color_cache.unseen.fg
} else if self.cursor_pos.2 == idx {
self.color_cache.highlighted.fg
} else if idx % 2 == 0 {
self.color_cache.even.fg
} else {
if self.cursor_pos.2 == idx {
Color::Byte(246)
} else if self.selection[&i] {
Color::Byte(210)
} else if !envelope.is_seen() {
Color::Byte(251)
} else {
self.data_columns.columns[0][(0, idx)].bg()
}
self.color_cache.odd.fg
};
let bg_color = if self.cursor_pos.2 == idx {
self.color_cache.highlighted.bg
} else if self.selection[&i] {
self.color_cache.selected.bg
} else if !envelope.is_seen() {
self.color_cache.unseen.bg
} else if idx % 2 == 0 {
self.color_cache.even.bg
} else {
self.color_cache.odd.bg
};
let attrs = if self.cursor_pos.2 == idx {
self.color_cache.highlighted.attrs
} else if self.selection[&i] {
self.color_cache.selected.attrs
} else if !envelope.is_seen() {
self.color_cache.unseen.attrs
} else if idx % 2 == 0 {
self.color_cache.even.attrs
} else {
self.color_cache.odd.attrs
};
let (upper_left, bottom_right) = area;
change_colors(grid, area, fg_color, bg_color);
let x = get_x(upper_left)
+ self.data_columns.widths[0]
+ self.data_columns.widths[1]
+ self.data_columns.widths[2]
+ 3 * 2;
for c in grid.row_iter(
get_x(upper_left)..(get_x(bottom_right) + 1),
get_y(upper_left),
) {
grid[c].set_fg(fg_color).set_bg(bg_color).set_attrs(attrs);
}
copy_area(
grid,
&self.data_columns.columns[3],
@ -166,7 +181,7 @@ impl ListingTrait for PlainListing {
),
);
for c in grid.row_iter(x..(x + self.data_columns.widths[3]), get_y(upper_left)) {
grid[c].set_bg(bg_color);
grid[c].set_bg(bg_color).set_attrs(attrs);
}
return;
}
@ -495,6 +510,7 @@ impl PlainListing {
force_draw: true,
unfocused: false,
view: MailView::default(),
color_cache: ColorCache::default(),
movement: None,
id: ComponentId::new_v4(),
@ -571,6 +587,23 @@ impl PlainListing {
return;
};
self.color_cache = ColorCache {
unseen: crate::conf::value(context, "mail.listing.plain.unseen"),
highlighted: crate::conf::value(context, "mail.listing.plain.highlighted"),
even: crate::conf::value(context, "mail.listing.plain.even"),
odd: crate::conf::value(context, "mail.listing.plain.odd"),
selected: crate::conf::value(context, "mail.listing.plain.selected"),
attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
..self.color_cache
};
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
self.color_cache.highlighted.attrs |= Attr::Reverse;
}
// Get mailbox as a reference.
//
match context.accounts[self.cursor_pos.0].status(self.folder_hash) {

289
ui/src/components/utilities.rs

@ -764,7 +764,13 @@ impl StatusBar {
}
}
fn draw_status_bar(&mut self, grid: &mut CellBuffer, area: Area, context: &mut Context) {
let attribute = crate::conf::value(context, "status.bar");
let mut attribute = crate::conf::value(context, "status.bar");
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
attribute.attrs |= Attr::Reverse;
}
let (x, y) = write_string_to_grid(
&self.status,
grid,
@ -774,7 +780,7 @@ impl StatusBar {
area,
None,
);
for c in grid.row_iter_from(x.., y) {
for c in grid.row_iter(x..(get_x(bottom_right!(area)) + 1), y) {
grid[c]
.set_ch(' ')
.set_fg(attribute.fg)
@ -789,7 +795,7 @@ impl StatusBar {
get_x(bottom_right!(area)),
)
{
grid[(x, y)].set_attrs(Attr::Bold);
grid[(x, y)].set_attrs(attribute.attrs | Attr::Bold);
}
}
let noto_colors = crate::conf::value(context, "status.notification");
@ -1369,21 +1375,29 @@ impl Tabbed {
clear_area(grid, area);
return;
}
let mut tab_focused_attribute = crate::conf::value(context, "tab.focused");
let tab_unfocused_attribute = crate::conf::value(context, "tab.unfocused");
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
tab_focused_attribute.attrs |= Attr::Reverse;
}
let mut x = get_x(upper_left);
let y: usize = get_y(upper_left);
for (idx, c) in self.children.iter().enumerate() {
let (fg, bg) = if idx == self.cursor_pos {
(Color::Default, Color::Default)
let ThemeAttribute { fg, bg, attrs } = if idx == self.cursor_pos {
tab_focused_attribute
} else {
(Color::Byte(15), Color::Byte(8))
tab_unfocused_attribute
};
let (x_, _y_) = write_string_to_grid(
&format!(" {} ", c),
grid,
fg,
bg,
Attr::Default,
attrs,
(set_x(upper_left, x), bottom_right!(area)),
None,
);
@ -1408,9 +1422,11 @@ impl Tabbed {
}
if self.cursor_pos == self.children.len() - 1 {
cslice[(y * cols) + x].set_ch('▍');
cslice[(y * cols) + x].set_fg(Color::Byte(8));
cslice[(y * cols) + x].set_bg(Color::Default);
cslice[(y * cols) + x]
.set_ch('▍')
.set_fg(tab_unfocused_attribute.bg)
.set_bg(tab_unfocused_attribute.fg)
.set_attrs(tab_unfocused_attribute.attrs);
}
context.dirty_areas.push_back(area);
@ -1838,6 +1854,13 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
fn process_event(&mut self, event: &mut UIEvent, context: &mut Context) -> bool {
let (width, height) = self.content.size();
let shortcuts = self.get_shortcuts(context);
let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted");
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
highlighted_attrs.attrs |= Attr::Reverse;
}
match (event, self.cursor) {
(UIEvent::Input(Key::Char('\n')), _) if self.single_only => {
/* User can only select one entry, so Enter key finalises the selection */
@ -1893,34 +1916,34 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
(UIEvent::Input(Key::Up), SelectorCursor::Entry(c)) if c > 0 => {
if self.single_only {
// Redraw selection
change_colors(
&mut self.content,
((2, c + 2), (width - 2, c + 2)),
Color::Default,
Color::Default,
);
change_colors(
&mut self.content,
((2, c + 1), (width - 2, c + 1)),
Color::Default,
Color::Byte(8),
);
for c in self.content.row_iter(2..(width - 2), c + 2) {
self.content[c]
.set_fg(Color::Default)
.set_bg(Color::Default)
.set_attrs(Attr::Default);
}
for c in self.content.row_iter(2..(width - 2), c + 1) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
self.entries[c].1 = false;
self.entries[c - 1].1 = true;
} else {
// Redraw cursor
change_colors(
&mut self.content,
((2, c + 2), (4, c + 2)),
Color::Default,
Color::Default,
);
change_colors(
&mut self.content,
((2, c + 1), (4, c + 1)),
Color::Default,
Color::Byte(8),
);
for c in self.content.row_iter(2..4, c + 2) {
self.content[c]
.set_fg(Color::Default)
.set_bg(Color::Default)
.set_attrs(Attr::Default);
}
for c in self.content.row_iter(2..4, c + 1) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
}
self.cursor = SelectorCursor::Entry(c - 1);
self.dirty = true;
@ -1930,23 +1953,31 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
| (UIEvent::Input(ref key), SelectorCursor::Cancel)
if shortcut!(key == shortcuts["general"]["scroll_up"]) =>
{
change_colors(
&mut self.content,
(
((width - "OK Cancel".len()) / 2, height - 3),
(width - 1, height - 3),
),
Color::Default,
Color::Default,
);
for c in self.content.row_iter(
((width - "OK Cancel".len()) / 2)..(width - 1),
height - 3,
) {
self.content[c]
.set_fg(Color::Default)
.set_bg(Color::Default)
.set_attrs(Attr::Default);
}
let c = self.entries.len().saturating_sub(1);
self.cursor = SelectorCursor::Entry(c);
change_colors(
&mut self.content,
((2, c + 2), (4, c + 2)),
Color::Default,
Color::Byte(8),
);
let mut highlighted_attrs =
crate::conf::value(context, "widgets.options.highlighted");
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
highlighted_attrs.attrs |= Attr::Reverse;
}
for c in self.content.row_iter(2..4, c + 2) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
self.dirty = true;
return true;
}
@ -1956,34 +1987,34 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
{
if self.single_only {
// Redraw selection
change_colors(
&mut self.content,
((2, c + 2), (width - 2, c + 2)),
Color::Default,
Color::Default,
);
change_colors(
&mut self.content,
((2, c + 3), (width - 2, c + 3)),
Color::Default,
Color::Byte(8),
);
for c in self.content.row_iter(2..(width - 2), c + 2) {
self.content[c]
.set_fg(Color::Default)
.set_bg(Color::Default)
.set_attrs(Attr::Default);
}
for c in self.content.row_iter(2..(width - 2), c + 3) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
self.entries[c].1 = false;
self.entries[c + 1].1 = true;
} else {
// Redraw cursor
change_colors(
&mut self.content,
((2, c + 2), (4, c + 2)),
Color::Default,
Color::Default,
);
change_colors(
&mut self.content,
((2, c + 3), (4, c + 3)),
Color::Default,
Color::Byte(8),
);
for c in self.content.row_iter(2..4, c + 2) {
self.content[c]
.set_fg(Color::Default)
.set_bg(Color::Default)
.set_attrs(Attr::Default);
}
for c in self.content.row_iter(2..4, c + 3) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
}
self.cursor = SelectorCursor::Entry(c + 1);
self.dirty = true;
@ -1993,21 +2024,21 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
if !self.single_only && shortcut!(key == shortcuts["general"]["scroll_down"]) =>
{
self.cursor = SelectorCursor::Ok;
change_colors(
&mut self.content,
((2, c + 2), (4, c + 2)),
Color::Default,
Color::Default,
);
change_colors(
&mut self.content,
(
((width - "OK Cancel".len()) / 2, height - 3),
((width - "OK Cancel".len()) / 2 + 1, height - 3),
),
Color::Default,
Color::Byte(8),
);
for c in self.content.row_iter(2..4, c + 2) {
self.content[c]
.set_fg(Color::Default)
.set_bg(Color::Default)
.set_attrs(Attr::Default);
}
for c in self.content.row_iter(
((width - "OK Cancel".len()) / 2)..((width - "OK Cancel".len()) / 2 + 1),
height - 3,
) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
self.dirty = true;
return true;
}
@ -2015,24 +2046,25 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
if shortcut!(key == shortcuts["general"]["scroll_right"]) =>
{
self.cursor = SelectorCursor::Cancel;
change_colors(
&mut self.content,
(
((width - "OK Cancel".len()) / 2, height - 3),
((width - "OK Cancel".len()) / 2 + 1, height - 3),
),
Color::Default,
Color::Default,
);
change_colors(
&mut self.content,
(
((width - "OK Cancel".len()) / 2 + 6, height - 3),
((width - "OK Cancel".len()) / 2 + 11, height - 3),
),
Color::Default,
Color::Byte(8),
);
for c in self.content.row_iter(
((width - "OK Cancel".len()) / 2)..((width - "OK Cancel".len()) / 2 + 1),
height - 3,
) {
self.content[c]
.set_fg(Color::Default)
.set_bg(Color::Default)
.set_attrs(Attr::Default);
}
for c in self.content.row_iter(
((width - "OK Cancel".len()) / 2 + 6)
..((width - "OK Cancel".len()) / 2 + 11),
height - 3,
) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
self.dirty = true;
return true;
}
@ -2040,15 +2072,15 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Component for Selector<T> {
if shortcut!(key == shortcuts["general"]["scroll_left"]) =>
{
self.cursor = SelectorCursor::Ok;
change_colors(
&mut self.content,
(
((width - "OK Cancel".len()) / 2, height - 3),
((width - "OK Cancel".len()) / 2 + 1, height - 3),
),
Color::Default,
Color::Byte(8),
);
for c in self.content.row_iter(
((width - "OK Cancel".len()) / 2)..((width - "OK Cancel".len()) / 2 + 1),
height - 3,
) {
self.content[c]
.set_fg(highlighted_attrs.fg)
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
change_colors(
&mut self.content,
(
@ -2213,6 +2245,13 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Selector<T> {
None,
);
}
let mut highlighted_attrs = crate::conf::value(context, "widgets.options.highlighted");
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
highlighted_attrs.attrs |= Attr::Reverse;
}
if single_only {
for (i, e) in entries.iter().enumerate() {
write_string_to_grid(
@ -2220,11 +2259,15 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Selector<T> {
&mut content,
Color::Default,
if i == 0 {
Color::Byte(8)
highlighted_attrs.bg
} else {
Color::Default
},
Attr::Default,
if i == 0 {
highlighted_attrs.attrs
} else {
Attr::Default
},
((2, i + 2), (width - 1, i + 2)),
None,
);
@ -2241,9 +2284,15 @@ impl<T: PartialEq + Debug + Clone + Sync + Send> Selector<T> {
None,
);
if i == 0 {
content[(2, i + 2)].set_bg(Color::Byte(8));
content[(3, i + 2)].set_bg(Color::Byte(8));
content[(4, i + 2)].set_bg(Color::Byte(8));
content[(2, i + 2)]
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
content[(3, i + 2)]
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
content[(4, i + 2)]
.set_bg(highlighted_attrs.bg)
.set_attrs(highlighted_attrs.attrs);
}
}
write_string_to_grid(

35
ui/src/components/utilities/widgets.rs

@ -375,15 +375,16 @@ impl Component for FormWidget {
),
);
let label_attrs = crate::conf::value(context, "widgets.form.label");
for (i, k) in self.layout.iter().enumerate() {
let v = self.fields.get_mut(k).unwrap();
/* Write field label */
write_string_to_grid(
k.as_str(),
grid,
Color::Default,
Color::Default,
Attr::Bold,
label_attrs.fg,
label_attrs.bg,
label_attrs.attrs,
(
pos_inc(upper_left, (1, i)),
set_y(bottom_right, i + get_y(upper_left)),
@ -403,15 +404,25 @@ impl Component for FormWidget {
/* Highlight if necessary */
if i == self.cursor {
if self.focus == FormFocus::Fields {
change_colors(
grid,
(
pos_inc(upper_left, (0, i)),
set_y(bottom_right, i + get_y(upper_left)),
),
Color::Default,
Color::Byte(246),
);
let mut field_attrs =
crate::conf::value(context, "widgets.form.highlighted");
if std::env::var("NO_COLOR").is_ok()
&& (context.settings.terminal.use_color.is_false()
|| context.settings.terminal.use_color.is_internal())
{
field_attrs.attrs |= Attr::Reverse;
}
for row in grid.bounds_iter((
pos_inc(upper_left, (0, i)),
set_y(bottom_right, i + get_y(upper_left)),
)) {
for c in row {
grid[c]
.set_fg(field_attrs.fg)
.set_bg(field_attrs.bg)
.set_attrs(field_attrs.attrs);
}
}
}
if self.focus == FormFocus::TextInput {
v.draw_cursor(

3
ui/src/conf/terminal.rs

@ -21,6 +21,7 @@
use super::deserializers::non_empty_string;
use super::Theme;
use super::ToggleFlag;
/// Settings for terminal display
#[derive(Debug, Deserialize, Clone, Serialize)]
@ -30,6 +31,7 @@ pub struct TerminalSettings {
pub theme: String,
pub themes: Theme,
pub ascii_drawing: bool,
pub use_color: ToggleFlag,
#[serde(deserialize_with = "non_empty_string")]
pub window_title: Option<String>,
}
@ -40,6 +42,7 @@ impl Default for TerminalSettings {
theme: "dark".to_string(),
themes: Theme::default(),
ascii_drawing: false,
use_color: ToggleFlag::InternalVal(true),
window_title: Some("meli".to_string()),
}
}

66
ui/src/conf/themes.rs

@ -149,6 +149,10 @@ const DEFAULT_KEYS: &'static [&'static str] = &[
"tab.focused",
"tab.unfocused",
"tab.bar",
"widgets.form.label",
"widgets.form.field",
"widgets.form.highlighted",
"widgets.options.highlighted",
"mail.sidebar",
"mail.sidebar_unread_count",
"mail.sidebar_index",
@ -166,6 +170,8 @@ const DEFAULT_KEYS: &'static [&'static str] = &[
"mail.listing.plain.even",
"mail.listing.plain.odd",
"mail.listing.plain.unseen",
"mail.listing.plain.selected",
"mail.listing.plain.highlighted",
"mail.listing.conversations",
"mail.listing.conversations.subject",
"mail.listing.conversations.from",
@ -216,6 +222,12 @@ impl From<Color> for ThemeValue<Color> {
}
}
impl From<Attr> for ThemeValue<Attr> {
fn from(from: Attr) -> Self {
ThemeValue::Value(from)
}
}
impl Default for ThemeValue<Color> {
fn default() -> Self {
ThemeValue::Value(Color::Default)
@ -440,8 +452,16 @@ impl Default for Theme {
add!("status.notification", dark = { fg: Color::Byte(219), bg: Color::Byte(88) }, light = { fg: Color::Byte(219), bg: Color::Byte(88) });
add!("tab.focused");
add!("tab.unfocused");
add!("tab.unfocused", dark = { fg: Color::Byte(15), bg: Color::Byte(8), }, light = { fg: Color::Byte(15), bg: Color::Byte(8), });
add!("tab.bar");
add!(
"widgets.form.label",
dark = { attrs: Attr::Bold },
light = { attrs: Attr::Bold }
);
add!("widgets.form.field");
add!("widgets.form.highlighted", light = { bg: Color::Byte(246) }, dark = { bg: Color::Byte(246) });
add!("widgets.options.highlighted", light = { bg: Color::Byte(8) }, dark = { bg: Color::Byte(8) });
/* Mail Sidebar */
@ -598,13 +618,45 @@ impl Default for Theme {
}
);
add!("mail.listing.plain.even");
/* PlainListing */
add!("mail.listing.plain.even",
dark = {
bg: Color::Byte(236)
},
light = {
bg: Color::Byte(252)
}
);
add!("mail.listing.plain.odd");
add!("mail.listing.plain.unseen");
add!("mail.listing.conversations.subject");
add!("mail.listing.conversations.from");
add!("mail.listing.conversations.date");
add!("mail.listing.conversations.unseen_padding");
add!(
"mail.listing.plain.unseen",
dark = {
fg: Color::Byte(0),
bg: Color::Byte(251)
},
light = {
fg: Color::Byte(0),
bg: Color::Byte(251)
}
);
add!("mail.listing.plain.selected",
dark = {
bg: Color::Byte(210)
},
light = {
bg: Color::Byte(210)
}
);
add!(
"mail.listing.plain.highlighted",
dark = {
bg: Color::Byte(246)
},
light = {
bg: Color::Byte(244)
}
);
add!(
"mail.view.headers",

44
ui/src/state.rs

@ -179,6 +179,7 @@ pub struct State {
draw_rate_limit: RateLimit,
stdout: Option<StateStdout>,
child: Option<ForkType>,
draw_horizontal_segment_fn: fn(&mut State, usize, usize, usize) -> (),
pub mode: UIMode,
components: Vec<Box<dyn Component>>,
pub context: Context,
@ -274,6 +275,14 @@ impl State {
components: Vec::with_capacity(1),
timer,
draw_rate_limit: RateLimit::new(1, 3),
draw_horizontal_segment_fn: if env::var("NO_COLOR").is_ok()
&& (settings.terminal.use_color.is_false()
|| settings.terminal.use_color.is_internal())
{
State::draw_horizontal_segment_no_color
} else {
State::draw_horizontal_segment
},
context: Context {
accounts,
@ -457,18 +466,18 @@ impl State {
continue;
}
if let Some((x_start, x_end)) = segment.take() {
self.draw_horizontal_segment(x_start, x_end, y);
(self.draw_horizontal_segment_fn)(self, x_start, x_end, y);
}
match segment {
ref mut s @ None => {
*s = Some((*x_start, *x_end));
}
ref mut s @ Some(_) if s.unwrap().1 < *x_start => {
self.draw_horizontal_segment(s.unwrap().0, s.unwrap().1, y);
(self.draw_horizontal_segment_fn)(self, s.unwrap().0, s.unwrap().1, y);
*s = Some((*x_start, *x_end));
}
ref mut s @ Some(_) if s.unwrap().1 < *x_end => {
self.draw_horizontal_segment(s.unwrap().0, s.unwrap().1, y);
(self.draw_horizontal_segment_fn)(self, s.unwrap().0, s.unwrap().1, y);
*s = Some((s.unwrap().1, *x_end));
}
Some((_, ref mut x)) => {
@ -477,7 +486,7 @@ impl State {
}
}
if let Some((x_start, x_end)) = segment {
self.draw_horizontal_segment(x_start, x_end, y);
(self.draw_horizontal_segment_fn)(self, x_start, x_end, y);
}
}
self.flush();
@ -521,6 +530,33 @@ impl State {
}
}
fn draw_horizontal_segment_no_color(&mut self, x_start: usize, x_end: usize, y: usize) {
write!(
self.stdout(),
"{}",
cursor::Goto(x_start as u16 + 1, (y + 1) as u16)
)
.unwrap();
let mut current_attrs = Attr::Default;
let Self {
ref grid,
ref mut stdout,
..
} = self;
let stdout = stdout.as_mut().unwrap();
write!(stdout, "\x1B[m").unwrap();
for x in x_start..=x_end {
let c = &grid[(x, y)];
if c.attrs() != current_attrs {
c.attrs().write(current_attrs, stdout).unwrap();
current_attrs = c.attrs();
}
if !c.empty() {
write!(stdout, "{}", c.ch()).unwrap();
}
}
}
/// Draw the entire screen from scratch.
pub fn render(&mut self) {
self.update_size();

48
ui/src/terminal/cells.rs

@ -366,19 +366,6 @@ impl CellBuffer {
RowIterator { row, col: 0..0 }
}
}
/// row_iter() but with `RangeFrom`.
/// See `RowIterator` documentation.
pub fn row_iter_from(&self, bounds: std::ops::RangeFrom<usize>, row: usize) -> RowIterator {
if row < self.rows {
RowIterator {
row,
col: std::cmp::min(self.cols.saturating_sub(1), bounds.start)..self.cols,
}
} else {
RowIterator { row, col: 0..0 }
}
}
}
impl Deref for CellBuffer {
@ -1407,6 +1394,24 @@ pub enum Attr {
BoldReverseUnderline = 0b111,
}
impl core::ops::BitOr for Attr {
type Output = Attr;
fn bitor(self, rhs: Self) -> Self::Output {
match self as u8 | rhs as u8 {
0b000 => Attr::Default,
0b001 => Attr::Bold,
0b100 => Attr::Underline,
0b011 => Attr::BoldUnderline,
0b010 => Attr::Reverse,
0b101 => Attr::BoldReverse,
0b110 => Attr::UnderlineReverse,
0b111 => Attr::BoldReverseUnderline,
_ => unsafe { std::hint::unreachable_unchecked() },
}
}
}
impl core::ops::BitAnd for Attr {
type Output = bool;
@ -1415,6 +1420,23 @@ impl core::ops::BitAnd for Attr {
}
}
impl core::ops::BitOrAssign for Attr {
fn bitor_assign(&mut self, rhs: Attr) {
use Attr::*;
*self = match *self as u8 | rhs as u8 {
0b000 => Default,
0b001 => Bold,
0b100 => Underline,
0b011 => BoldUnderline,
0b010 => Reverse,
0b101 => BoldReverse,
0b110 => UnderlineReverse,
0b111 => BoldReverseUnderline,
_ => unsafe { std::hint::unreachable_unchecked() },
};
}
}
impl Default for Attr {
fn default() -> Self {
Attr::Default

Loading…
Cancel
Save