ui/themes: add ThemeAttribute

Consolidate {fg,bg} color theme settings to ThemeAttribute and add Attr
(bold, etc).
async
Manos Pitsidianakis 2020-01-23 19:52:54 +02:00
parent f787eb75b6
commit 1e2b3c073d
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
9 changed files with 508 additions and 428 deletions

View File

@ -46,29 +46,21 @@ pub struct DataColumns {
#[derive(Debug, Default)] #[derive(Debug, Default)]
/// Save theme colors to avoid looking them up again and again from settings /// Save theme colors to avoid looking them up again and again from settings
struct ColorCache { struct ColorCache {
unseen_fg: Color, unseen: ThemeAttribute,
unseen_bg: Color, highlighted: ThemeAttribute,
highlighted_fg: Color, even: ThemeAttribute,
highlighted_bg: Color, odd: ThemeAttribute,
even_fg: Color, selected: ThemeAttribute,
even_bg: Color, attachment_flag: ThemeAttribute,
odd_fg: Color, thread_snooze_flag: ThemeAttribute,
odd_bg: Color,
selected_bg: Color,
attachment_flag_fg: Color,
thread_snooze_flag_fg: Color,
/* Conversations */ /* Conversations */
fg: Color, general: ThemeAttribute,
bg: Color, subject: ThemeAttribute,
subject_fg: Color, from: ThemeAttribute,
subject_bg: Color, date: ThemeAttribute,
from_fg: Color, padding: ThemeAttribute,
from_bg: Color, unseen_padding: ThemeAttribute,
date_fg: Color,
date_bg: Color,
padding: Color,
unseen_padding: Color,
} }
#[derive(Debug)] #[derive(Debug)]
@ -1067,37 +1059,39 @@ impl Listing {
) = if must_highlight_account { ) = if must_highlight_account {
if self.cursor_pos.1 == idx { if self.cursor_pos.1 == idx {
( (
crate::conf::color(context, "mail.sidebar_highlighted_fg"), crate::conf::value(context, "mail.sidebar_highlighted").fg,
crate::conf::color(context, "mail.sidebar_highlighted_bg"), crate::conf::value(context, "mail.sidebar_highlighted").bg,
crate::conf::color(context, "mail.sidebar_highlighted_index_fg"), crate::conf::value(context, "mail.sidebar_highlighted_index").fg,
crate::conf::color(context, "mail.sidebar_highlighted_index_bg"), crate::conf::value(context, "mail.sidebar_highlighted_index").bg,
crate::conf::color(context, "mail.sidebar_highlighted_unread_count_fg"), crate::conf::value(context, "mail.sidebar_highlighted_unread_count").fg,
crate::conf::color(context, "mail.sidebar_highlighted_unread_count_bg"), crate::conf::value(context, "mail.sidebar_highlighted_unread_count").bg,
) )
} else { } else {
( (
crate::conf::color(context, "mail.sidebar_highlighted_account_fg"), crate::conf::value(context, "mail.sidebar_highlighted_account").fg,
crate::conf::color(context, "mail.sidebar_highlighted_account_bg"), crate::conf::value(context, "mail.sidebar_highlighted_account").bg,
crate::conf::color(context, "mail.sidebar_highlighted_account_index_fg"), crate::conf::value(context, "mail.sidebar_highlighted_account_index").fg,
crate::conf::color(context, "mail.sidebar_highlighted_account_index_bg"), crate::conf::value(context, "mail.sidebar_highlighted_account_index").bg,
crate::conf::color( crate::conf::value(
context, context,
"mail.sidebar_highlighted_account_unread_count_fg", "mail.sidebar_highlighted_account_unread_count",
), )
crate::conf::color( .fg,
crate::conf::value(
context, context,
"mail.sidebar_highlighted_account_unread_count_bg", "mail.sidebar_highlighted_account_unread_count",
), )
.bg,
) )
} }
} else { } else {
( (
crate::conf::color(context, "mail.sidebar_fg"), crate::conf::value(context, "mail.sidebar").fg,
crate::conf::color(context, "mail.sidebar_bg"), crate::conf::value(context, "mail.sidebar").bg,
crate::conf::color(context, "mail.sidebar_index_fg"), crate::conf::value(context, "mail.sidebar_index").fg,
crate::conf::color(context, "mail.sidebar_index_bg"), crate::conf::value(context, "mail.sidebar_index").bg,
crate::conf::color(context, "mail.sidebar_unread_count_fg"), crate::conf::value(context, "mail.sidebar_unread_count").fg,
crate::conf::color(context, "mail.sidebar_unread_count_bg"), crate::conf::value(context, "mail.sidebar_unread_count").bg,
) )
}; };

View File

@ -127,24 +127,24 @@ impl ListingTrait for CompactListing {
let thread = threads.thread_ref(thread_hash); let thread = threads.thread_ref(thread_hash);
let fg_color = if thread.unseen() > 0 { let fg_color = if thread.unseen() > 0 {
self.color_cache.unseen_fg self.color_cache.unseen.fg
} else if self.cursor_pos.2 == idx { } else if self.cursor_pos.2 == idx {
self.color_cache.highlighted_fg self.color_cache.highlighted.fg
} else if idx % 2 == 0 { } else if idx % 2 == 0 {
self.color_cache.even_fg self.color_cache.even.fg
} else { } else {
self.color_cache.odd_fg self.color_cache.odd.fg
}; };
let bg_color = if self.cursor_pos.2 == idx { let bg_color = if self.cursor_pos.2 == idx {
self.color_cache.highlighted_bg self.color_cache.highlighted.bg
} else if self.selection[&thread_hash] { } else if self.selection[&thread_hash] {
self.color_cache.selected_bg self.color_cache.selected.bg
} else if thread.unseen() > 0 { } else if thread.unseen() > 0 {
self.color_cache.unseen_bg self.color_cache.unseen.bg
} else if idx % 2 == 0 { } else if idx % 2 == 0 {
self.color_cache.even_bg self.color_cache.even.bg
} else { } else {
self.color_cache.odd_bg self.color_cache.odd.bg
}; };
let (upper_left, bottom_right) = area; let (upper_left, bottom_right) = area;
@ -348,7 +348,7 @@ impl ListingTrait for CompactListing {
let c = &self.data_columns.columns[0][(0, r + top_idx)]; let c = &self.data_columns.columns[0][(0, r + top_idx)];
if self.selection[&thread_hash] { if self.selection[&thread_hash] {
(c.fg(), self.color_cache.selected_bg) (c.fg(), self.color_cache.selected.bg)
} else { } else {
(c.fg(), c.bg()) (c.fg(), c.bg())
} }
@ -632,20 +632,13 @@ impl CompactListing {
}; };
self.color_cache = ColorCache { self.color_cache = ColorCache {
unseen_fg: crate::conf::color(context, "mail.listing.compact.unseen_fg"), unseen: crate::conf::value(context, "mail.listing.compact.unseen"),
unseen_bg: crate::conf::color(context, "mail.listing.compact.unseen_bg"), highlighted: crate::conf::value(context, "mail.listing.compact.highlighted"),
highlighted_fg: crate::conf::color(context, "mail.listing.compact.highlighted_fg"), even: crate::conf::value(context, "mail.listing.compact.even"),
highlighted_bg: crate::conf::color(context, "mail.listing.compact.highlighted_bg"), odd: crate::conf::value(context, "mail.listing.compact.odd"),
even_fg: crate::conf::color(context, "mail.listing.compact.even_fg"), selected: crate::conf::value(context, "mail.listing.compact.selected"),
even_bg: crate::conf::color(context, "mail.listing.compact.even_bg"), attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
odd_fg: crate::conf::color(context, "mail.listing.compact.odd_fg"), thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
odd_bg: crate::conf::color(context, "mail.listing.compact.odd_bg"),
selected_bg: crate::conf::color(context, "mail.listing.compact.selected_bg"),
attachment_flag_fg: crate::conf::color(context, "mail.listing.attachment_flag_fg"),
thread_snooze_flag_fg: crate::conf::color(
context,
"mail.listing.thread_snooze_flag_fg",
),
..self.color_cache ..self.color_cache
}; };
@ -821,11 +814,11 @@ impl CompactListing {
} }
let thread = threads.thread_ref(thread); let thread = threads.thread_ref(thread);
let (fg_color, bg_color) = if thread.unseen() > 0 { let (fg_color, bg_color) = if thread.unseen() > 0 {
(self.color_cache.unseen_fg, self.color_cache.unseen_bg) (self.color_cache.unseen.fg, self.color_cache.unseen.bg)
} else if idx % 2 == 0 { } else if idx % 2 == 0 {
(self.color_cache.even_fg, self.color_cache.even_bg) (self.color_cache.even.fg, self.color_cache.even.bg)
} else { } else {
(self.color_cache.odd_fg, self.color_cache.odd_bg) (self.color_cache.odd.fg, self.color_cache.odd.bg)
}; };
let (x, _) = write_string_to_grid( let (x, _) = write_string_to_grid(
&idx.to_string(), &idx.to_string(),
@ -917,17 +910,17 @@ impl CompactListing {
match (thread.snoozed(), thread.has_attachments()) { match (thread.snoozed(), thread.has_attachments()) {
(true, true) => { (true, true) => {
self.data_columns.columns[3][(0, idx)] self.data_columns.columns[3][(0, idx)]
.set_fg(self.color_cache.attachment_flag_fg); .set_fg(self.color_cache.attachment_flag.fg);
self.data_columns.columns[3][(2, idx)] self.data_columns.columns[3][(2, idx)]
.set_fg(self.color_cache.thread_snooze_flag_fg); .set_fg(self.color_cache.thread_snooze_flag.fg);
} }
(true, false) => { (true, false) => {
self.data_columns.columns[3][(0, idx)] self.data_columns.columns[3][(0, idx)]
.set_fg(self.color_cache.thread_snooze_flag_fg); .set_fg(self.color_cache.thread_snooze_flag.fg);
} }
(false, true) => { (false, true) => {
self.data_columns.columns[3][(0, idx)] self.data_columns.columns[3][(0, idx)]
.set_fg(self.color_cache.attachment_flag_fg); .set_fg(self.color_cache.attachment_flag.fg);
} }
(false, false) => {} (false, false) => {}
} }
@ -982,11 +975,11 @@ impl CompactListing {
} }
let idx = self.order[&thread_hash]; let idx = self.order[&thread_hash];
let (fg_color, bg_color) = if thread.unseen() > 0 { let (fg_color, bg_color) = if thread.unseen() > 0 {
(self.color_cache.unseen_fg, self.color_cache.unseen_bg) (self.color_cache.unseen.fg, self.color_cache.unseen.bg)
} else if idx % 2 == 0 { } else if idx % 2 == 0 {
(self.color_cache.even_fg, self.color_cache.even_bg) (self.color_cache.even.fg, self.color_cache.even.bg)
} else { } else {
(self.color_cache.odd_fg, self.color_cache.odd_bg) (self.color_cache.odd.fg, self.color_cache.odd.bg)
}; };
let envelope: EnvelopeRef = account.collection.get_env(env_hash); let envelope: EnvelopeRef = account.collection.get_env(env_hash);
let strings = self.make_entry_string(&envelope, context, threads, thread_hash); let strings = self.make_entry_string(&envelope, context, threads, thread_hash);
@ -1092,14 +1085,14 @@ impl CompactListing {
} }
match (thread.snoozed(), thread.has_attachments()) { match (thread.snoozed(), thread.has_attachments()) {
(true, true) => { (true, true) => {
columns[3][(0, idx)].set_fg(self.color_cache.attachment_flag_fg); columns[3][(0, idx)].set_fg(self.color_cache.attachment_flag.fg);
columns[3][(2, idx)].set_fg(self.color_cache.thread_snooze_flag_fg); columns[3][(2, idx)].set_fg(self.color_cache.thread_snooze_flag.fg);
} }
(true, false) => { (true, false) => {
columns[3][(0, idx)].set_fg(self.color_cache.thread_snooze_flag_fg); columns[3][(0, idx)].set_fg(self.color_cache.thread_snooze_flag.fg);
} }
(false, true) => { (false, true) => {
columns[3][(0, idx)].set_fg(self.color_cache.attachment_flag_fg); columns[3][(0, idx)].set_fg(self.color_cache.attachment_flag.fg);
} }
(false, false) => {} (false, false) => {}
} }

View File

@ -106,18 +106,18 @@ impl ListingTrait for ConversationsListing {
let thread = threads.thread_ref(thread_hash); let thread = threads.thread_ref(thread_hash);
let fg_color = if thread.unseen() > 0 { let fg_color = if thread.unseen() > 0 {
self.color_cache.unseen_fg self.color_cache.unseen.fg
} else { } else {
self.color_cache.fg self.color_cache.general.fg
}; };
let bg_color = if self.cursor_pos.2 == idx { let bg_color = if self.cursor_pos.2 == idx {
self.color_cache.highlighted_bg self.color_cache.highlighted.bg
} else if self.selection[&thread_hash] { } else if self.selection[&thread_hash] {
self.color_cache.selected_bg self.color_cache.selected.bg
} else if thread.unseen() > 0 { } else if thread.unseen() > 0 {
self.color_cache.unseen_bg self.color_cache.unseen.bg
} else { } else {
self.color_cache.bg self.color_cache.general.bg
}; };
copy_area( copy_area(
@ -127,7 +127,7 @@ impl ListingTrait for ConversationsListing {
((0, 3 * idx), pos_dec(self.content.size(), (1, 1))), ((0, 3 * idx), pos_dec(self.content.size(), (1, 1))),
); );
let padding_fg = self.color_cache.padding; let padding_fg = self.color_cache.padding.fg;
let (upper_left, bottom_right) = area; let (upper_left, bottom_right) = area;
let width = self.content.size().0; let width = self.content.size().0;
@ -292,7 +292,7 @@ impl ListingTrait for ConversationsListing {
/* fill any remaining columns, if our view is wider than self.content */ /* fill any remaining columns, if our view is wider than self.content */
let width = self.content.size().0; let width = self.content.size().0;
let padding_fg = self.color_cache.padding; let padding_fg = self.color_cache.padding.fg;
if width < width!(area) { if width < width!(area) {
let y_offset = get_y(upper_left); let y_offset = get_y(upper_left);
@ -554,34 +554,19 @@ impl ConversationsListing {
}; };
self.color_cache = ColorCache { self.color_cache = ColorCache {
fg: crate::conf::color(context, "mail.listing.conversations.fg"), general: crate::conf::value(context, "mail.listing.conversations"),
bg: crate::conf::color(context, "mail.listing.conversations.bg"), subject: crate::conf::value(context, "mail.listing.conversations.subject"),
subject_fg: crate::conf::color(context, "mail.listing.conversations.subject_fg"), from: crate::conf::value(context, "mail.listing.conversations.from"),
subject_bg: crate::conf::color(context, "mail.listing.conversations.subject_bg"), date: crate::conf::value(context, "mail.listing.conversations.date"),
from_fg: crate::conf::color(context, "mail.listing.conversations.from_fg"), padding: crate::conf::value(context, "mail.listing.conversations.padding"),
from_bg: crate::conf::color(context, "mail.listing.conversations.from_bg"), unseen: crate::conf::value(context, "mail.listing.conversations.unseen"),
date_fg: crate::conf::color(context, "mail.listing.conversations.date_fg"), unseen_padding: crate::conf::value(
date_bg: crate::conf::color(context, "mail.listing.conversations.date_bg"),
padding: crate::conf::color(context, "mail.listing.conversations.padding"),
unseen_fg: crate::conf::color(context, "mail.listing.conversations.unseen_fg"),
unseen_bg: crate::conf::color(context, "mail.listing.conversations.unseen_bg"),
unseen_padding: crate::conf::color(
context, context,
"mail.listing.conversations.unseen_padding", "mail.listing.conversations.unseen_padding",
), ),
highlighted_fg: crate::conf::color( highlighted: crate::conf::value(context, "mail.listing.conversations.highlighted"),
context, attachment_flag: crate::conf::value(context, "mail.listing.attachment_flag"),
"mail.listing.conversations.highlighted_fg", thread_snooze_flag: crate::conf::value(context, "mail.listing.thread_snooze_flag"),
),
highlighted_bg: crate::conf::color(
context,
"mail.listing.conversations.highlighted_bg",
),
attachment_flag_fg: crate::conf::color(context, "mail.listing.attachment_flag_fg"),
thread_snooze_flag_fg: crate::conf::color(
context,
"mail.listing.thread_snooze_flag_fg",
),
..self.color_cache ..self.color_cache
}; };
@ -711,7 +696,7 @@ impl ConversationsListing {
self.content = self.content =
CellBuffer::new_with_context(width, 4 * rows.len(), Cell::with_char(' '), context); CellBuffer::new_with_context(width, 4 * rows.len(), Cell::with_char(' '), context);
let padding_fg = self.color_cache.padding; let padding_fg = self.color_cache.padding.fg;
for ((idx, (thread, root_env_hash)), strings) in rows { for ((idx, (thread, root_env_hash)), strings) in rows {
if !context.accounts[self.cursor_pos.0].contains_key(root_env_hash) { if !context.accounts[self.cursor_pos.0].contains_key(root_env_hash) {
@ -719,14 +704,14 @@ impl ConversationsListing {
} }
let thread = threads.thread_ref(thread); let thread = threads.thread_ref(thread);
let fg_color = if thread.unseen() > 0 { let fg_color = if thread.unseen() > 0 {
self.color_cache.unseen_fg self.color_cache.unseen.fg
} else { } else {
self.color_cache.fg self.color_cache.general.fg
}; };
let bg_color = if thread.unseen() > 0 { let bg_color = if thread.unseen() > 0 {
self.color_cache.unseen_bg self.color_cache.unseen.bg
} else { } else {
self.color_cache.bg self.color_cache.general.bg
}; };
/* draw flags */ /* draw flags */
let (x, _) = write_string_to_grid( let (x, _) = write_string_to_grid(
@ -891,16 +876,16 @@ impl ConversationsListing {
let env_hash = threads.thread_nodes()[&thread_node_hash].message().unwrap(); let env_hash = threads.thread_nodes()[&thread_node_hash].message().unwrap();
let fg_color = if thread.unseen() > 0 { let fg_color = if thread.unseen() > 0 {
self.color_cache.unseen_fg self.color_cache.unseen.fg
} else { } else {
self.color_cache.fg self.color_cache.general.fg
}; };
let bg_color = if thread.unseen() > 0 { let bg_color = if thread.unseen() > 0 {
self.color_cache.unseen_bg self.color_cache.unseen.bg
} else { } else {
self.color_cache.bg self.color_cache.general.bg
}; };
let padding_fg = self.color_cache.padding; let padding_fg = self.color_cache.padding.fg;
let mut from_address_list = Vec::new(); let mut from_address_list = Vec::new();
let mut from_address_set: std::collections::HashSet<Vec<u8>> = let mut from_address_set: std::collections::HashSet<Vec<u8>> =
std::collections::HashSet::new(); std::collections::HashSet::new();

View File

@ -371,8 +371,7 @@ impl Component for MailView {
let account = &context.accounts[self.coordinates.0]; let account = &context.accounts[self.coordinates.0];
let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2);
let headers_fg = crate::conf::color(context, "mail.view.headers_fg"); let headers = crate::conf::value(context, "mail.view.headers");
let headers_bg = crate::conf::color(context, "mail.view.headers_bg");
if self.mode == ViewMode::Raw { if self.mode == ViewMode::Raw {
clear_area(grid, area); clear_area(grid, area);
@ -394,9 +393,9 @@ impl Component for MailView {
let (_x, _y) = write_string_to_grid( let (_x, _y) = write_string_to_grid(
&$string, &$string,
grid, grid,
headers_fg, headers.fg,
headers_bg, headers.bg,
Attr::Default, headers.attrs,
(set_y(upper_left, y), bottom_right), (set_y(upper_left, y), bottom_right),
Some(get_x(upper_left)), Some(get_x(upper_left)),
); );
@ -446,9 +445,9 @@ impl Component for MailView {
let (_x, _) = write_string_to_grid( let (_x, _) = write_string_to_grid(
"List-ID: ", "List-ID: ",
grid, grid,
headers_fg, headers.fg,
headers_bg, headers.bg,
Attr::Default, headers.attrs,
(set_y(upper_left, y), bottom_right), (set_y(upper_left, y), bottom_right),
None, None,
); );
@ -474,9 +473,9 @@ impl Component for MailView {
let (_x, _y) = write_string_to_grid( let (_x, _y) = write_string_to_grid(
" Available actions: [ ", " Available actions: [ ",
grid, grid,
headers_fg, headers.fg,
headers_bg, headers.bg,
Attr::Default, headers.attrs,
((x, y), bottom_right), ((x, y), bottom_right),
Some(get_x(upper_left)), Some(get_x(upper_left)),
); );
@ -527,8 +526,9 @@ impl Component for MailView {
grid[(x - 2, y)].set_ch(' '); grid[(x - 2, y)].set_ch(' ');
} }
if x > 0 { if x > 0 {
grid[(x - 1, y)].set_fg(headers_fg); grid[(x - 1, y)].set_fg(headers.fg);
grid[(x - 1, y)].set_bg(headers_bg); grid[(x - 1, y)].set_bg(headers.bg);
grid[(x - 1, y)].set_attrs(headers.attrs);
grid[(x - 1, y)].set_ch(']'); grid[(x - 1, y)].set_ch(']');
} }
} }
@ -646,10 +646,7 @@ impl Component for MailView {
.map(|v| String::from_utf8_lossy(v).into_owned()) .map(|v| String::from_utf8_lossy(v).into_owned())
.unwrap_or_else(|e| e.to_string()) .unwrap_or_else(|e| e.to_string())
}; };
let colors = PagerColors { let colors = crate::conf::value(context, "mail.view.body");
fg: crate::conf::color(context, "mail.view.body_fg"),
bg: crate::conf::color(context, "mail.view.body_bg"),
};
self.pager = Pager::from_string(text, Some(context), None, None, colors); self.pager = Pager::from_string(text, Some(context), None, None, colors);
} }
ViewMode::Ansi(ref buf) => { ViewMode::Ansi(ref buf) => {
@ -676,10 +673,7 @@ impl Component for MailView {
} else { } else {
self.pager.cursor_pos() self.pager.cursor_pos()
}; };
let colors = PagerColors { let colors = crate::conf::value(context, "mail.view.body");
fg: crate::conf::color(context, "mail.view.body_fg"),
bg: crate::conf::color(context, "mail.view.body_bg"),
};
self.pager = self.pager =
Pager::from_string(text, Some(context), Some(cursor_pos), None, colors); Pager::from_string(text, Some(context), Some(cursor_pos), None, colors);
self.subview = None; self.subview = None;

View File

@ -339,10 +339,7 @@ impl Component for EnvelopeView {
} else { } else {
self.pager.as_ref().map(Pager::cursor_pos) self.pager.as_ref().map(Pager::cursor_pos)
}; };
let colors = PagerColors { let colors = crate::conf::value(context, "mail.view.body");
fg: crate::conf::color(context, "mail.view.body_fg"),
bg: crate::conf::color(context, "mail.view.body_bg"),
};
self.pager = Some(Pager::from_string( self.pager = Some(Pager::from_string(
text, text,
Some(context), Some(context),
@ -421,10 +418,7 @@ impl Component for EnvelopeView {
match u.content_type() { match u.content_type() {
ContentType::MessageRfc822 => { ContentType::MessageRfc822 => {
self.mode = ViewMode::Subview; self.mode = ViewMode::Subview;
let colors = PagerColors { let colors = crate::conf::value(context, "mail.view.body");
fg: crate::conf::color(context, "mail.view.body_fg"),
bg: crate::conf::color(context, "mail.view.body_bg"),
};
self.subview = Some(Box::new(Pager::from_string( self.subview = Some(Box::new(Pager::from_string(
String::from_utf8_lossy(&decode_rec(u, None)).to_string(), String::from_utf8_lossy(&decode_rec(u, None)).to_string(),
Some(context), Some(context),

View File

@ -109,10 +109,7 @@ impl HtmlView {
s s
}); });
} }
let colors = PagerColors { let colors = crate::conf::value(context, "mail.view.body");
fg: crate::conf::color(context, "mail.view.body_fg"),
bg: crate::conf::color(context, "mail.view.body_bg"),
};
let pager = Pager::from_string(display_text, None, None, None, colors); let pager = Pager::from_string(display_text, None, None, None, colors);
HtmlView { pager, bytes, id } HtmlView { pager, bytes, id }
} }

View File

@ -264,12 +264,6 @@ impl Component for VSplit {
} }
} }
#[derive(Debug, Default, Clone, Copy)]
pub struct PagerColors {
pub fg: Color,
pub bg: Color,
}
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub enum PageMovement { pub enum PageMovement {
Up(usize), Up(usize),
@ -295,7 +289,7 @@ pub struct Pager {
minimum_width: usize, minimum_width: usize,
dirty: bool, dirty: bool,
colors: PagerColors, colors: ThemeAttribute,
initialised: bool, initialised: bool,
content: CellBuffer, content: CellBuffer,
movement: Option<PageMovement>, movement: Option<PageMovement>,
@ -346,7 +340,7 @@ impl Pager {
context: Option<&Context>, context: Option<&Context>,
cursor_pos: Option<usize>, cursor_pos: Option<usize>,
mut width: Option<usize>, mut width: Option<usize>,
colors: PagerColors, colors: ThemeAttribute,
) -> Self { ) -> Self {
let pager_filter: Option<&String> = if let Some(context) = context { let pager_filter: Option<&String> = if let Some(context) = context {
context.settings.pager.filter.as_ref() context.settings.pager.filter.as_ref()
@ -435,7 +429,7 @@ impl Pager {
text: &str, text: &str,
cursor_pos: Option<usize>, cursor_pos: Option<usize>,
width: Option<usize>, width: Option<usize>,
colors: PagerColors, colors: ThemeAttribute,
) -> Self { ) -> Self {
let lines: Vec<String> = if let Some(width) = width { let lines: Vec<String> = if let Some(width) = width {
text.split_lines(width) text.split_lines(width)
@ -477,7 +471,7 @@ impl Pager {
..Default::default() ..Default::default()
} }
} }
pub fn print_string(content: &mut CellBuffer, lines: Vec<String>, colors: PagerColors) { pub fn print_string(content: &mut CellBuffer, lines: Vec<String>, colors: ThemeAttribute) {
let width = content.size().0; let width = content.size().0;
debug!(colors); debug!(colors);
for (i, l) in lines.iter().enumerate() { for (i, l) in lines.iter().enumerate() {

View File

@ -19,7 +19,7 @@
* along with meli. If not, see <http://www.gnu.org/licenses/>. * along with meli. If not, see <http://www.gnu.org/licenses/>.
*/ */
use crate::terminal::Color; use crate::terminal::{Attr, Color};
use crate::Context; use crate::Context;
use melib::Result; use melib::Result;
use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
@ -27,7 +27,7 @@ use std::borrow::Cow;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
#[inline(always)] #[inline(always)]
pub fn color(context: &Context, key: &'static str) -> Color { pub fn value(context: &Context, key: &'static str) -> ThemeAttribute {
let theme = match context.settings.terminal.theme.as_str() { let theme = match context.settings.terminal.theme.as_str() {
"light" => &context.settings.terminal.themes.light, "light" => &context.settings.terminal.themes.light,
"dark" | _ => &context.settings.terminal.themes.dark, "dark" | _ => &context.settings.terminal.themes.dark,
@ -35,13 +35,78 @@ pub fn color(context: &Context, key: &'static str) -> Color {
unlink(theme, &Cow::from(key)) unlink(theme, &Cow::from(key))
} }
#[inline(always)]
pub fn fg_color(context: &Context, key: &'static str) -> Color {
let theme = match context.settings.terminal.theme.as_str() {
"light" => &context.settings.terminal.themes.light,
"dark" | _ => &context.settings.terminal.themes.dark,
};
unlink_fg(theme, &Cow::from(key))
}
#[inline(always)]
pub fn bg_color(context: &Context, key: &'static str) -> Color {
let theme = match context.settings.terminal.theme.as_str() {
"light" => &context.settings.terminal.themes.light,
"dark" | _ => &context.settings.terminal.themes.dark,
};
unlink_bg(theme, &Cow::from(key))
}
#[inline(always)]
pub fn attrs(context: &Context, key: &'static str) -> Attr {
let theme = match context.settings.terminal.theme.as_str() {
"light" => &context.settings.terminal.themes.light,
"dark" | _ => &context.settings.terminal.themes.dark,
};
unlink_attrs(theme, &Cow::from(key))
}
#[inline(always)] #[inline(always)]
fn unlink<'k, 't: 'k>( fn unlink<'k, 't: 'k>(
theme: &'t HashMap<Cow<'static, str>, ThemeValue>, theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
key: &'k Cow<'static, str>,
) -> ThemeAttribute {
ThemeAttribute {
fg: unlink_fg(theme, key),
bg: unlink_bg(theme, key),
attrs: unlink_attrs(theme, key),
}
}
#[inline(always)]
fn unlink_fg<'k, 't: 'k>(
theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
mut key: &'k Cow<'static, str>, mut key: &'k Cow<'static, str>,
) -> Color { ) -> Color {
loop { loop {
match &theme[key] { match &theme[key].fg {
ThemeValue::Link(ref new_key) => key = new_key,
ThemeValue::Value(val) => return *val,
}
}
}
#[inline(always)]
fn unlink_bg<'k, 't: 'k>(
theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
mut key: &'k Cow<'static, str>,
) -> Color {
loop {
match &theme[key].bg {
ThemeValue::Link(ref new_key) => key = new_key,
ThemeValue::Value(val) => return *val,
}
}
}
#[inline(always)]
fn unlink_attrs<'k, 't: 'k>(
theme: &'t HashMap<Cow<'static, str>, ThemeAttributeInner>,
mut key: &'k Cow<'static, str>,
) -> Attr {
loop {
match &theme[key].attrs {
ThemeValue::Link(ref new_key) => key = new_key, ThemeValue::Link(ref new_key) => key = new_key,
ThemeValue::Value(val) => return *val, ThemeValue::Value(val) => return *val,
} }
@ -49,94 +114,116 @@ fn unlink<'k, 't: 'k>(
} }
const DEFAULT_KEYS: &'static [&'static str] = &[ const DEFAULT_KEYS: &'static [&'static str] = &[
"general.background", "general",
"general.foreground", "general.status_bar",
"general.status_bar_fg", "general.tab_focused",
"general.status_bar_bg", "general.tab_unfocused",
"general.tab_focused_fg", "general.tab_bar",
"general.tab_focused_bg", "mail.sidebar",
"general.tab_unfocused_fg", "mail.sidebar_unread_count",
"general.tab_unfocused_bg", "mail.sidebar_index",
"general.tab_bar_bg", "mail.sidebar_highlighted",
"mail.sidebar_fg", "mail.sidebar_highlighted_unread_count",
"mail.sidebar_bg", "mail.sidebar_highlighted_index",
"mail.sidebar_unread_count_fg", "mail.sidebar_highlighted_account",
"mail.sidebar_unread_count_bg", "mail.sidebar_highlighted_account_unread_count",
"mail.sidebar_index_fg", "mail.sidebar_highlighted_account_index",
"mail.sidebar_index_bg", "mail.listing.compact.even",
"mail.sidebar_highlighted_fg", "mail.listing.compact.odd",
"mail.sidebar_highlighted_bg", "mail.listing.compact.unseen",
"mail.sidebar_highlighted_unread_count_fg", "mail.listing.compact.selected",
"mail.sidebar_highlighted_unread_count_bg", "mail.listing.compact.highlighted",
"mail.sidebar_highlighted_index_fg", "mail.listing.plain.even",
"mail.sidebar_highlighted_index_bg", "mail.listing.plain.odd",
"mail.sidebar_highlighted_account_fg", "mail.listing.plain.unseen",
"mail.sidebar_highlighted_account_bg", "mail.listing.conversations",
"mail.sidebar_highlighted_account_unread_count_fg", "mail.listing.conversations.subject",
"mail.sidebar_highlighted_account_unread_count_bg", "mail.listing.conversations.from",
"mail.sidebar_highlighted_account_index_fg", "mail.listing.conversations.date",
"mail.sidebar_highlighted_account_index_bg",
"mail.listing.compact.even_fg",
"mail.listing.compact.even_bg",
"mail.listing.compact.odd_fg",
"mail.listing.compact.odd_bg",
"mail.listing.compact.unseen_fg",
"mail.listing.compact.unseen_bg",
"mail.listing.compact.selected_fg",
"mail.listing.compact.selected_bg",
"mail.listing.compact.highlighted_fg",
"mail.listing.compact.highlighted_bg",
"mail.listing.plain.even_fg",
"mail.listing.plain.even_bg",
"mail.listing.plain.odd_fg",
"mail.listing.plain.odd_bg",
"mail.listing.plain.unseen_fg",
"mail.listing.plain.unseen_bg",
"mail.listing.conversations.fg",
"mail.listing.conversations.bg",
"mail.listing.conversations.subject_fg",
"mail.listing.conversations.subject_bg",
"mail.listing.conversations.from_fg",
"mail.listing.conversations.from_bg",
"mail.listing.conversations.date_fg",
"mail.listing.conversations.date_bg",
"mail.listing.conversations.padding", "mail.listing.conversations.padding",
"mail.listing.conversations.unseen_fg", "mail.listing.conversations.unseen",
"mail.listing.conversations.unseen_bg",
"mail.listing.conversations.unseen_padding", "mail.listing.conversations.unseen_padding",
"mail.listing.conversations.highlighted_fg", "mail.listing.conversations.highlighted",
"mail.listing.conversations.highlighted_bg", "mail.listing.conversations.selected",
"mail.listing.conversations.selected_fg", "mail.view.headers",
"mail.listing.conversations.selected_bg", "mail.view.body",
"mail.view.headers_fg", "mail.listing.attachment_flag",
"mail.view.headers_bg", "mail.listing.thread_snooze_flag",
"mail.view.body_fg",
"mail.view.body_bg",
"mail.listing.attachment_flag_fg",
"mail.listing.attachment_flag_bg",
"mail.listing.thread_snooze_flag_fg",
"mail.listing.thread_snooze_flag_bg",
]; ];
#[derive(Debug, Clone, Default, Copy, Serialize, Deserialize)]
pub struct ThemeAttribute {
pub fg: Color,
pub bg: Color,
pub attrs: Attr,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ThemeAttributeInner {
#[serde(default)]
fg: ThemeValue<Color>,
#[serde(default)]
bg: ThemeValue<Color>,
#[serde(default)]
attrs: ThemeValue<Attr>,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum ThemeValue { pub enum ThemeValue<T> {
Value(Color), Value(T),
Link(Cow<'static, str>), Link(Cow<'static, str>),
} }
impl From<Color> for ThemeValue { impl<T> From<&'static str> for ThemeValue<T> {
fn from(from: &'static str) -> Self {
ThemeValue::Link(from.into())
}
}
impl From<Color> for ThemeValue<Color> {
fn from(from: Color) -> Self { fn from(from: Color) -> Self {
ThemeValue::Value(from) ThemeValue::Value(from)
} }
} }
impl Default for ThemeValue { impl Default for ThemeValue<Color> {
fn default() -> Self { fn default() -> Self {
ThemeValue::Value(Color::Default) ThemeValue::Value(Color::Default)
} }
} }
impl<'de> Deserialize<'de> for ThemeValue { impl Default for ThemeValue<Attr> {
fn default() -> Self {
ThemeValue::Value(Attr::Default)
}
}
impl<'de> Deserialize<'de> for ThemeValue<Attr> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if let Ok(s) = <Attr>::deserialize(deserializer) {
Ok(ThemeValue::Value(s))
} else {
Err(de::Error::custom("invalid theme attribute value"))
}
}
}
impl<T: Serialize> Serialize for ThemeValue<T> {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
ThemeValue::Value(s) => s.serialize(serializer),
_ => unreachable!(),
}
}
}
impl<'de> Deserialize<'de> for ThemeValue<Color> {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error> fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where where
D: Deserializer<'de>, D: Deserializer<'de>,
@ -156,9 +243,9 @@ impl<'de> Deserialize<'de> for ThemeValue {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct Theme { pub struct Theme {
#[serde(default)] #[serde(default)]
pub light: HashMap<Cow<'static, str>, ThemeValue>, pub light: HashMap<Cow<'static, str>, ThemeAttributeInner>,
#[serde(default)] #[serde(default)]
pub dark: HashMap<Cow<'static, str>, ThemeValue>, pub dark: HashMap<Cow<'static, str>, ThemeAttributeInner>,
} }
impl Theme { impl Theme {
@ -190,236 +277,217 @@ impl Default for Theme {
let mut dark = HashMap::default(); let mut dark = HashMap::default();
macro_rules! add { macro_rules! add {
($key:literal, light=$light:literal, dark=$dark:literal) => { ($key:literal, $($theme:ident={ $($name:ident : $val:expr),*$(,)? }),*$(,)?) => {
light.insert($key.into(), ThemeValue::Link($light.into())); $($theme.insert($key.into(), ThemeAttributeInner {
dark.insert($key.into(), ThemeValue::Link($dark.into())); $($name: $val.into()),*
}; ,..ThemeAttributeInner::default() }));*
($key:literal, dark=$dark:literal, light=$light:literal) => {
light.insert($key.into(), ThemeValue::Link($light.into()));
dark.insert($key.into(), ThemeValue::Link($dark.into()));
};
($key:literal, light=$light:literal) => {
light.insert($key.into(), $ThemeValue::Link(light);)
dark.insert($key.into(), ThemeValue::Value(Color::Default));
};
($key:literal, dark=$dark:literal) => {
light.insert($key.into(),ThemeValue::Value(Color::Default));
dark.insert($key.into(), ThemeValue::Link($dark.into()));
};
($key:literal, light=$light:expr, dark=$dark:expr) => {
light.insert($key.into(), ThemeValue::Value($light));
dark.insert($key.into(), ThemeValue::Value($dark));
};
($key:literal, dark=$dark:expr, light=$light:expr) => {
light.insert($key.into(), ThemeValue::Value($light));
dark.insert($key.into(), ThemeValue::Value($dark));
};
($key:literal, light=$light:expr) => {
light.insert($key.into(), $ThemeValue::Value(light);)
dark.insert($key.into(), ThemeValue::Value(Color::Default));
};
($key:literal, dark=$dark:expr) => {
light.insert($key.into(),ThemeValue::Value(Color::Default));
dark.insert($key.into(), ThemeValue::Value($dark));
}; };
($key:literal) => { ($key:literal) => {
light.insert($key.into(), ThemeValue::Value(Color::Default)); light.insert($key.into(), ThemeAttributeInner::default());
dark.insert($key.into(), ThemeValue::Value(Color::Default)); dark.insert($key.into(), ThemeAttributeInner::default());
}; };
} }
add!("general");
add!("general.background");
add!("general.foreground");
/* /*
"general.status_bar_fg", "general.status_bar",
"general.status_bar_bg", "general.tab_focused",
"general.tab_focused_fg", "general.tab_unfocused",
"general.tab_focused_bg", "general.tab_bar",
"general.tab_unfocused_fg",
"general.tab_unfocused_bg",
"general.tab_bar_bg",
*/ */
/* Mail Sidebar */ /* Mail Sidebar */
add!("mail.sidebar_fg"); add!("mail.sidebar");
add!("mail.sidebar_bg"); add!("mail.sidebar_unread_count", dark = { fg: Color::Byte(243) });
add!("mail.sidebar_unread_count_fg", dark = Color::Byte(243)); add!("mail.sidebar_index", dark = { fg: Color::Byte(243) });
add!("mail.sidebar_unread_count_bg"); add!("mail.sidebar_highlighted", dark = { fg: Color::Byte(233), bg: Color::Byte(15) });
add!("mail.sidebar_index_fg", dark = Color::Byte(243));
add!("mail.sidebar_index_bg");
add!("mail.sidebar_highlighted_fg", dark = Color::Byte(233));
add!("mail.sidebar_highlighted_bg", dark = Color::Byte(15));
add!( add!(
"mail.sidebar_highlighted_unread_count_fg", "mail.sidebar_highlighted_unread_count",
light = "mail.sidebar_highlighted_fg", light = {
dark = "mail.sidebar_highlighted_fg" fg: "mail.sidebar_highlighted",
bg: "mail.sidebar_highlighted"
},
dark = {
fg: "mail.sidebar_highlighted",
bg: "mail.sidebar_highlighted"
}
); );
add!( add!(
"mail.sidebar_highlighted_unread_count_bg", "mail.sidebar_highlighted_index",
light = "mail.sidebar_highlighted_bg", light = {
dark = "mail.sidebar_highlighted_bg" fg: "mail.sidebar_index",
bg: "mail.sidebar_highlighted",
},
dark = {
fg: "mail.sidebar_index",
bg: "mail.sidebar_highlighted",
},
); );
add!( add!(
"mail.sidebar_highlighted_index_fg", "mail.sidebar_highlighted_account",
light = "mail.sidebar_index_fg", dark = {
dark = "mail.sidebar_index_fg" fg: Color::Byte(15),
bg: Color::Byte(233),
}
); );
add!( add!(
"mail.sidebar_highlighted_index_bg", "mail.sidebar_highlighted_account_unread_count",
light = "mail.sidebar_highlighted_bg", light = {
dark = "mail.sidebar_highlighted_bg" fg: "mail.sidebar_unread_count",
bg: "mail.sidebar_highlighted_account",
},
dark = {
fg: "mail.sidebar_unread_count",
bg: "mail.sidebar_highlighted_account"
}
); );
add!( add!(
"mail.sidebar_highlighted_account_fg", "mail.sidebar_highlighted_account_index",
dark = Color::Byte(15) light = {
); fg: "mail.sidebar_index",
add!( bg: "mail.sidebar_highlighted_account"
"mail.sidebar_highlighted_account_bg", },
dark = Color::Byte(233) dark = {
); fg: "mail.sidebar_index",
add!( bg: "mail.sidebar_highlighted_account"
"mail.sidebar_highlighted_account_unread_count_fg", }
light = "mail.sidebar_unread_count_fg",
dark = "mail.sidebar_unread_count_fg"
);
add!(
"mail.sidebar_highlighted_account_unread_count_bg",
light = "mail.sidebar_highlighted_account_bg",
dark = "mail.sidebar_highlighted_account_bg"
);
add!(
"mail.sidebar_highlighted_account_index_fg",
light = "mail.sidebar_index_fg",
dark = "mail.sidebar_index_fg"
);
add!(
"mail.sidebar_highlighted_account_index_bg",
light = "mail.sidebar_highlighted_account_bg",
dark = "mail.sidebar_highlighted_account_bg"
); );
/* CompactListing */ /* CompactListing */
add!("mail.listing.compact.even_fg"); add!("mail.listing.compact.even",
add!( dark = {
"mail.listing.compact.even_bg", bg: Color::Byte(236)
dark = Color::Byte(236), },
light = Color::Byte(252) light = {
bg: Color::Byte(252)
}
); );
add!("mail.listing.compact.odd_fg"); add!("mail.listing.compact.odd");
add!("mail.listing.compact.odd_bg");
add!( add!(
"mail.listing.compact.unseen_fg", "mail.listing.compact.unseen",
dark = Color::Byte(0), dark = {
light = Color::Byte(0) fg: Color::Byte(0),
bg: Color::Byte(251)
},
light = {
fg: Color::Byte(0),
bg: Color::Byte(251)
}
);
add!("mail.listing.compact.selected",
dark = {
bg: Color::Byte(210)
},
light = {
bg: Color::Byte(210)
}
); );
add!( add!(
"mail.listing.compact.unseen_bg", "mail.listing.compact.highlighted",
dark = Color::Byte(251), dark = {
light = Color::Byte(251) bg: Color::Byte(246)
); },
add!("mail.listing.compact.selected_fg"); light = {
add!( bg: Color::Byte(244)
"mail.listing.compact.selected_bg", }
dark = Color::Byte(210),
light = Color::Byte(210)
);
add!("mail.listing.compact.highlighted_fg");
add!(
"mail.listing.compact.highlighted_bg",
dark = Color::Byte(246),
light = Color::Byte(244)
); );
/* ConversationsListing */ /* ConversationsListing */
add!("mail.listing.conversations.fg"); add!("mail.listing.conversations");
add!("mail.listing.conversations.bg"); add!("mail.listing.conversations.subject");
add!("mail.listing.conversations.subject_fg"); add!("mail.listing.conversations.from");
add!("mail.listing.conversations.subject_bg"); add!("mail.listing.conversations.date");
add!("mail.listing.conversations.from_fg");
add!("mail.listing.conversations.from_bg");
add!("mail.listing.conversations.date_fg");
add!("mail.listing.conversations.date_bg");
add!( add!(
"mail.listing.conversations.padding", "mail.listing.conversations.padding",
dark = Color::Byte(235), dark = {
light = Color::Byte(254) fg: Color::Byte(235),
bg: Color::Byte(235),
},
light = {
fg: Color::Byte(254),
bg: Color::Byte(254),
}
); );
add!( add!(
"mail.listing.conversations.unseen_padding", "mail.listing.conversations.unseen_padding",
dark = Color::Byte(235), dark = {
light = Color::Byte(254) fg: Color::Byte(235),
bg: Color::Byte(235),
},
light = {
fg: Color::Byte(254),
bg: Color::Byte(254),
}
); );
add!( add!(
"mail.listing.conversations.unseen_fg", "mail.listing.conversations.unseen",
dark = Color::Byte(0), dark = {
light = Color::Byte(0) fg: Color::Byte(0),
bg: Color::Byte(251)
},
light = {
fg: Color::Byte(0),
bg: Color::Byte(251)
}
); );
add!( add!(
"mail.listing.conversations.unseen_bg", "mail.listing.conversations.highlighted",
dark = Color::Byte(251), dark = {
light = Color::Byte(251) bg: Color::Byte(246),
},
light = {
bg: Color::Byte(246)
}
); );
add!("mail.listing.conversations.highlighted_fg"); add!("mail.listing.conversations.selected",
add!( dark = {
"mail.listing.conversations.highlighted_bg", bg: Color::Byte(210),
dark = Color::Byte(246), },
light = Color::Byte(246) light = {
); bg: Color::Byte(210)
add!("mail.listing.conversations.selected_fg"); }
add!(
"mail.listing.conversations.selected_bg",
dark = Color::Byte(210),
light = Color::Byte(210)
); );
/* /*
"mail.listing.plain.even_fg", "mail.listing.plain.even",
"mail.listing.plain.even_bg", "mail.listing.plain.odd",
"mail.listing.plain.odd_fg", "mail.listing.plain.unseen",
"mail.listing.plain.odd_bg", "mail.listing.conversations.subject",
"mail.listing.plain.unseen_fg", "mail.listing.conversations.from",
"mail.listing.plain.unseen_bg", "mail.listing.conversations.date",
"mail.listing.conversations.subject_fg",
"mail.listing.conversations.subject_bg",
"mail.listing.conversations.from_fg",
"mail.listing.conversations.from_bg",
"mail.listing.conversations.date_fg",
"mail.listing.conversations.date_bg",
"mail.listing.conversations.unseen_padding", "mail.listing.conversations.unseen_padding",
*/ */
add!( add!(
"mail.view.headers_fg", "mail.view.headers",
dark = Color::Byte(33), dark = {
light = Color::Black fg: Color::Byte(33),
},
light = {
fg: Color::Black,
}
); );
add!("mail.view.headers_bg"); add!("mail.view.body");
add!("mail.view.body_fg");
add!("mail.view.body_bg");
add!( add!(
"mail.listing.attachment_flag_fg", "mail.listing.attachment_flag",
light = Color::Byte(103), light = {
dark = Color::Byte(103) fg: Color::Byte(103),
},
dark = {
fg: Color::Byte(103)
}
); );
add!( add!(
"mail.listing.attachment_flag_bg", "mail.listing.thread_snooze_flag",
light = Color::Default, light = {
dark = Color::Default fg: Color::Red,
); },
dark = {
add!( fg: Color::Red,
"mail.listing.thread_snooze_flag_fg", }
light = Color::Red,
dark = Color::Red
);
add!(
"mail.listing.thread_snooze_flag_bg",
light = Color::Default,
dark = Color::Default
); );
Theme { light, dark } Theme { light, dark }
@ -431,21 +499,35 @@ impl Serialize for Theme {
where where
S: Serializer, S: Serializer,
{ {
let mut dark: HashMap<Cow<'static, str>, Color> = Default::default(); let mut dark: HashMap<Cow<'static, str>, ThemeAttribute> = Default::default();
let mut light: HashMap<Cow<'static, str>, Color> = Default::default(); let mut light: HashMap<Cow<'static, str>, ThemeAttribute> = Default::default();
for k in self.dark.keys() { for k in self.dark.keys() {
dark.insert(k.clone(), unlink(&self.dark, k)); dark.insert(
k.clone(),
ThemeAttribute {
fg: unlink_fg(&self.light, k),
bg: unlink_bg(&self.light, k),
attrs: unlink_attrs(&self.light, k),
},
);
} }
for k in self.light.keys() { for k in self.light.keys() {
light.insert(k.clone(), unlink(&self.light, k)); light.insert(
k.clone(),
ThemeAttribute {
fg: unlink_fg(&self.light, k),
bg: unlink_bg(&self.light, k),
attrs: unlink_attrs(&self.light, k),
},
);
} }
#[derive(Serialize)] #[derive(Serialize)]
struct ThemeSer { struct ThemeSer {
light: HashMap<Cow<'static, str>, Color>, light: HashMap<Cow<'static, str>, ThemeAttribute>,
dark: HashMap<Cow<'static, str>, Color>, dark: HashMap<Cow<'static, str>, ThemeAttribute>,
} }
use serde::ser::SerializeStruct; use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct("ThemeSer", 2)?; let mut s = serializer.serialize_struct("ThemeSer", 2)?;

View File

@ -1385,6 +1385,53 @@ pub enum Attr {
BoldReverseUnderline = 0b111, BoldReverseUnderline = 0b111,
} }
impl Default for Attr {
fn default() -> Self {
Attr::Default
}
}
impl<'de> Deserialize<'de> for Attr {
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
where
D: Deserializer<'de>,
{
if let Ok(s) = <String>::deserialize(deserializer) {
match s.as_str() {
"Default" => Ok(Attr::Default),
"Bold" => Ok(Attr::Bold),
"Underline" => Ok(Attr::Underline),
"BoldUnderline" => Ok(Attr::BoldUnderline),
"Reverse" => Ok(Attr::Reverse),
"BoldReverse" => Ok(Attr::BoldReverse),
"UnderlineReverse" => Ok(Attr::UnderlineReverse),
"BoldReverseUnderline" => Ok(Attr::BoldReverseUnderline),
_ => Err(de::Error::custom("invalid attr value")),
}
} else {
Err(de::Error::custom("invalid attr value"))
}
}
}
impl Serialize for Attr {
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
where
S: Serializer,
{
match self {
Attr::Default => serializer.serialize_str("Default"),
Attr::Bold => serializer.serialize_str("Bold"),
Attr::Underline => serializer.serialize_str("Underline"),
Attr::BoldUnderline => serializer.serialize_str("BoldUnderline"),
Attr::Reverse => serializer.serialize_str("Reverse"),
Attr::BoldReverse => serializer.serialize_str("BoldReverse"),
Attr::UnderlineReverse => serializer.serialize_str("UnderlineReverse"),
Attr::BoldReverseUnderline => serializer.serialize_str("BoldReverseUnderline"),
}
}
}
pub fn copy_area_with_break( pub fn copy_area_with_break(
grid_dest: &mut CellBuffer, grid_dest: &mut CellBuffer,
grid_src: &CellBuffer, grid_src: &CellBuffer,