From 1e2b3c073d8b6ba3634774c54ddcfd4d612c8acb Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 23 Jan 2020 19:52:54 +0200 Subject: [PATCH] ui/themes: add ThemeAttribute Consolidate {fg,bg} color theme settings to ThemeAttribute and add Attr (bold, etc). --- ui/src/components/mail/listing.rs | 80 ++- ui/src/components/mail/listing/compact.rs | 69 +- .../components/mail/listing/conversations.rs | 71 +- ui/src/components/mail/view.rs | 36 +- ui/src/components/mail/view/envelope.rs | 10 +- ui/src/components/mail/view/html.rs | 5 +- ui/src/components/utilities.rs | 14 +- ui/src/conf/themes.rs | 604 ++++++++++-------- ui/src/terminal/cells.rs | 47 ++ 9 files changed, 508 insertions(+), 428 deletions(-) diff --git a/ui/src/components/mail/listing.rs b/ui/src/components/mail/listing.rs index e317fcabe..24446e5de 100644 --- a/ui/src/components/mail/listing.rs +++ b/ui/src/components/mail/listing.rs @@ -46,29 +46,21 @@ pub struct DataColumns { #[derive(Debug, Default)] /// Save theme colors to avoid looking them up again and again from settings struct ColorCache { - unseen_fg: Color, - unseen_bg: Color, - highlighted_fg: Color, - highlighted_bg: Color, - even_fg: Color, - even_bg: Color, - odd_fg: Color, - odd_bg: Color, - selected_bg: Color, - attachment_flag_fg: Color, - thread_snooze_flag_fg: Color, + unseen: ThemeAttribute, + highlighted: ThemeAttribute, + even: ThemeAttribute, + odd: ThemeAttribute, + selected: ThemeAttribute, + attachment_flag: ThemeAttribute, + thread_snooze_flag: ThemeAttribute, /* Conversations */ - fg: Color, - bg: Color, - subject_fg: Color, - subject_bg: Color, - from_fg: Color, - from_bg: Color, - date_fg: Color, - date_bg: Color, - padding: Color, - unseen_padding: Color, + general: ThemeAttribute, + subject: ThemeAttribute, + from: ThemeAttribute, + date: ThemeAttribute, + padding: ThemeAttribute, + unseen_padding: ThemeAttribute, } #[derive(Debug)] @@ -1067,37 +1059,39 @@ impl Listing { ) = if must_highlight_account { if self.cursor_pos.1 == idx { ( - crate::conf::color(context, "mail.sidebar_highlighted_fg"), - crate::conf::color(context, "mail.sidebar_highlighted_bg"), - crate::conf::color(context, "mail.sidebar_highlighted_index_fg"), - crate::conf::color(context, "mail.sidebar_highlighted_index_bg"), - crate::conf::color(context, "mail.sidebar_highlighted_unread_count_fg"), - crate::conf::color(context, "mail.sidebar_highlighted_unread_count_bg"), + 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, ) } else { ( - crate::conf::color(context, "mail.sidebar_highlighted_account_fg"), - crate::conf::color(context, "mail.sidebar_highlighted_account_bg"), - crate::conf::color(context, "mail.sidebar_highlighted_account_index_fg"), - crate::conf::color(context, "mail.sidebar_highlighted_account_index_bg"), - crate::conf::color( + 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_unread_count_fg", - ), - crate::conf::color( + "mail.sidebar_highlighted_account_unread_count", + ) + .fg, + crate::conf::value( context, - "mail.sidebar_highlighted_account_unread_count_bg", - ), + "mail.sidebar_highlighted_account_unread_count", + ) + .bg, ) } } else { ( - crate::conf::color(context, "mail.sidebar_fg"), - crate::conf::color(context, "mail.sidebar_bg"), - crate::conf::color(context, "mail.sidebar_index_fg"), - crate::conf::color(context, "mail.sidebar_index_bg"), - crate::conf::color(context, "mail.sidebar_unread_count_fg"), - crate::conf::color(context, "mail.sidebar_unread_count_bg"), + 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, ) }; diff --git a/ui/src/components/mail/listing/compact.rs b/ui/src/components/mail/listing/compact.rs index fd66d7251..9fa5df4db 100644 --- a/ui/src/components/mail/listing/compact.rs +++ b/ui/src/components/mail/listing/compact.rs @@ -127,24 +127,24 @@ impl ListingTrait for CompactListing { let thread = threads.thread_ref(thread_hash); let fg_color = if thread.unseen() > 0 { - self.color_cache.unseen_fg + self.color_cache.unseen.fg } else if self.cursor_pos.2 == idx { - self.color_cache.highlighted_fg + self.color_cache.highlighted.fg } else if idx % 2 == 0 { - self.color_cache.even_fg + self.color_cache.even.fg } else { - self.color_cache.odd_fg + self.color_cache.odd.fg }; 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] { - self.color_cache.selected_bg + self.color_cache.selected.bg } else if thread.unseen() > 0 { - self.color_cache.unseen_bg + self.color_cache.unseen.bg } else if idx % 2 == 0 { - self.color_cache.even_bg + self.color_cache.even.bg } else { - self.color_cache.odd_bg + self.color_cache.odd.bg }; 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)]; if self.selection[&thread_hash] { - (c.fg(), self.color_cache.selected_bg) + (c.fg(), self.color_cache.selected.bg) } else { (c.fg(), c.bg()) } @@ -632,20 +632,13 @@ impl CompactListing { }; self.color_cache = ColorCache { - unseen_fg: crate::conf::color(context, "mail.listing.compact.unseen_fg"), - unseen_bg: crate::conf::color(context, "mail.listing.compact.unseen_bg"), - highlighted_fg: crate::conf::color(context, "mail.listing.compact.highlighted_fg"), - highlighted_bg: crate::conf::color(context, "mail.listing.compact.highlighted_bg"), - even_fg: crate::conf::color(context, "mail.listing.compact.even_fg"), - even_bg: crate::conf::color(context, "mail.listing.compact.even_bg"), - odd_fg: crate::conf::color(context, "mail.listing.compact.odd_fg"), - 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", - ), + unseen: crate::conf::value(context, "mail.listing.compact.unseen"), + highlighted: crate::conf::value(context, "mail.listing.compact.highlighted"), + even: crate::conf::value(context, "mail.listing.compact.even"), + odd: crate::conf::value(context, "mail.listing.compact.odd"), + selected: crate::conf::value(context, "mail.listing.compact.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 }; @@ -821,11 +814,11 @@ impl CompactListing { } let thread = threads.thread_ref(thread); 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 { - (self.color_cache.even_fg, self.color_cache.even_bg) + (self.color_cache.even.fg, self.color_cache.even.bg) } 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( &idx.to_string(), @@ -917,17 +910,17 @@ impl CompactListing { match (thread.snoozed(), thread.has_attachments()) { (true, true) => { 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)] - .set_fg(self.color_cache.thread_snooze_flag_fg); + .set_fg(self.color_cache.thread_snooze_flag.fg); } (true, false) => { 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) => { 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) => {} } @@ -982,11 +975,11 @@ impl CompactListing { } let idx = self.order[&thread_hash]; 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 { - (self.color_cache.even_fg, self.color_cache.even_bg) + (self.color_cache.even.fg, self.color_cache.even.bg) } 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 strings = self.make_entry_string(&envelope, context, threads, thread_hash); @@ -1092,14 +1085,14 @@ impl CompactListing { } match (thread.snoozed(), thread.has_attachments()) { (true, true) => { - 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][(0, idx)].set_fg(self.color_cache.attachment_flag.fg); + columns[3][(2, idx)].set_fg(self.color_cache.thread_snooze_flag.fg); } (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) => { - 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) => {} } diff --git a/ui/src/components/mail/listing/conversations.rs b/ui/src/components/mail/listing/conversations.rs index 8c3ddb4bf..9cf1bd243 100644 --- a/ui/src/components/mail/listing/conversations.rs +++ b/ui/src/components/mail/listing/conversations.rs @@ -106,18 +106,18 @@ impl ListingTrait for ConversationsListing { let thread = threads.thread_ref(thread_hash); let fg_color = if thread.unseen() > 0 { - self.color_cache.unseen_fg + self.color_cache.unseen.fg } else { - self.color_cache.fg + self.color_cache.general.fg }; 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] { - self.color_cache.selected_bg + self.color_cache.selected.bg } else if thread.unseen() > 0 { - self.color_cache.unseen_bg + self.color_cache.unseen.bg } else { - self.color_cache.bg + self.color_cache.general.bg }; copy_area( @@ -127,7 +127,7 @@ impl ListingTrait for ConversationsListing { ((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 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 */ 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) { let y_offset = get_y(upper_left); @@ -554,34 +554,19 @@ impl ConversationsListing { }; self.color_cache = ColorCache { - fg: crate::conf::color(context, "mail.listing.conversations.fg"), - bg: crate::conf::color(context, "mail.listing.conversations.bg"), - subject_fg: crate::conf::color(context, "mail.listing.conversations.subject_fg"), - subject_bg: crate::conf::color(context, "mail.listing.conversations.subject_bg"), - from_fg: crate::conf::color(context, "mail.listing.conversations.from_fg"), - from_bg: crate::conf::color(context, "mail.listing.conversations.from_bg"), - date_fg: crate::conf::color(context, "mail.listing.conversations.date_fg"), - 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( + general: crate::conf::value(context, "mail.listing.conversations"), + subject: crate::conf::value(context, "mail.listing.conversations.subject"), + from: crate::conf::value(context, "mail.listing.conversations.from"), + date: crate::conf::value(context, "mail.listing.conversations.date"), + padding: crate::conf::value(context, "mail.listing.conversations.padding"), + unseen: crate::conf::value(context, "mail.listing.conversations.unseen"), + unseen_padding: crate::conf::value( context, "mail.listing.conversations.unseen_padding", ), - highlighted_fg: crate::conf::color( - context, - "mail.listing.conversations.highlighted_fg", - ), - 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", - ), + highlighted: crate::conf::value(context, "mail.listing.conversations.highlighted"), + 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 }; @@ -711,7 +696,7 @@ impl ConversationsListing { self.content = 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 { 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 fg_color = if thread.unseen() > 0 { - self.color_cache.unseen_fg + self.color_cache.unseen.fg } else { - self.color_cache.fg + self.color_cache.general.fg }; let bg_color = if thread.unseen() > 0 { - self.color_cache.unseen_bg + self.color_cache.unseen.bg } else { - self.color_cache.bg + self.color_cache.general.bg }; /* draw flags */ let (x, _) = write_string_to_grid( @@ -891,16 +876,16 @@ impl ConversationsListing { let env_hash = threads.thread_nodes()[&thread_node_hash].message().unwrap(); let fg_color = if thread.unseen() > 0 { - self.color_cache.unseen_fg + self.color_cache.unseen.fg } else { - self.color_cache.fg + self.color_cache.general.fg }; let bg_color = if thread.unseen() > 0 { - self.color_cache.unseen_bg + self.color_cache.unseen.bg } 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_set: std::collections::HashSet> = std::collections::HashSet::new(); diff --git a/ui/src/components/mail/view.rs b/ui/src/components/mail/view.rs index 78458c641..4dac91e88 100644 --- a/ui/src/components/mail/view.rs +++ b/ui/src/components/mail/view.rs @@ -371,8 +371,7 @@ impl Component for MailView { let account = &context.accounts[self.coordinates.0]; let envelope: EnvelopeRef = account.collection.get_env(self.coordinates.2); - let headers_fg = crate::conf::color(context, "mail.view.headers_fg"); - let headers_bg = crate::conf::color(context, "mail.view.headers_bg"); + let headers = crate::conf::value(context, "mail.view.headers"); if self.mode == ViewMode::Raw { clear_area(grid, area); @@ -394,9 +393,9 @@ impl Component for MailView { let (_x, _y) = write_string_to_grid( &$string, grid, - headers_fg, - headers_bg, - Attr::Default, + headers.fg, + headers.bg, + headers.attrs, (set_y(upper_left, y), bottom_right), Some(get_x(upper_left)), ); @@ -446,9 +445,9 @@ impl Component for MailView { let (_x, _) = write_string_to_grid( "List-ID: ", grid, - headers_fg, - headers_bg, - Attr::Default, + headers.fg, + headers.bg, + headers.attrs, (set_y(upper_left, y), bottom_right), None, ); @@ -474,9 +473,9 @@ impl Component for MailView { let (_x, _y) = write_string_to_grid( " Available actions: [ ", grid, - headers_fg, - headers_bg, - Attr::Default, + headers.fg, + headers.bg, + headers.attrs, ((x, y), bottom_right), Some(get_x(upper_left)), ); @@ -527,8 +526,9 @@ impl Component for MailView { grid[(x - 2, y)].set_ch(' '); } if x > 0 { - grid[(x - 1, y)].set_fg(headers_fg); - grid[(x - 1, y)].set_bg(headers_bg); + grid[(x - 1, y)].set_fg(headers.fg); + grid[(x - 1, y)].set_bg(headers.bg); + grid[(x - 1, y)].set_attrs(headers.attrs); grid[(x - 1, y)].set_ch(']'); } } @@ -646,10 +646,7 @@ impl Component for MailView { .map(|v| String::from_utf8_lossy(v).into_owned()) .unwrap_or_else(|e| e.to_string()) }; - let colors = PagerColors { - fg: crate::conf::color(context, "mail.view.body_fg"), - bg: crate::conf::color(context, "mail.view.body_bg"), - }; + let colors = crate::conf::value(context, "mail.view.body"); self.pager = Pager::from_string(text, Some(context), None, None, colors); } ViewMode::Ansi(ref buf) => { @@ -676,10 +673,7 @@ impl Component for MailView { } else { self.pager.cursor_pos() }; - let colors = PagerColors { - fg: crate::conf::color(context, "mail.view.body_fg"), - bg: crate::conf::color(context, "mail.view.body_bg"), - }; + let colors = crate::conf::value(context, "mail.view.body"); self.pager = Pager::from_string(text, Some(context), Some(cursor_pos), None, colors); self.subview = None; diff --git a/ui/src/components/mail/view/envelope.rs b/ui/src/components/mail/view/envelope.rs index cddd42219..ef6321717 100644 --- a/ui/src/components/mail/view/envelope.rs +++ b/ui/src/components/mail/view/envelope.rs @@ -339,10 +339,7 @@ impl Component for EnvelopeView { } else { self.pager.as_ref().map(Pager::cursor_pos) }; - let colors = PagerColors { - fg: crate::conf::color(context, "mail.view.body_fg"), - bg: crate::conf::color(context, "mail.view.body_bg"), - }; + let colors = crate::conf::value(context, "mail.view.body"); self.pager = Some(Pager::from_string( text, Some(context), @@ -421,10 +418,7 @@ impl Component for EnvelopeView { match u.content_type() { ContentType::MessageRfc822 => { self.mode = ViewMode::Subview; - let colors = PagerColors { - fg: crate::conf::color(context, "mail.view.body_fg"), - bg: crate::conf::color(context, "mail.view.body_bg"), - }; + let colors = crate::conf::value(context, "mail.view.body"); self.subview = Some(Box::new(Pager::from_string( String::from_utf8_lossy(&decode_rec(u, None)).to_string(), Some(context), diff --git a/ui/src/components/mail/view/html.rs b/ui/src/components/mail/view/html.rs index 7e4ecbfc7..7d7502661 100644 --- a/ui/src/components/mail/view/html.rs +++ b/ui/src/components/mail/view/html.rs @@ -109,10 +109,7 @@ impl HtmlView { s }); } - let colors = PagerColors { - fg: crate::conf::color(context, "mail.view.body_fg"), - bg: crate::conf::color(context, "mail.view.body_bg"), - }; + let colors = crate::conf::value(context, "mail.view.body"); let pager = Pager::from_string(display_text, None, None, None, colors); HtmlView { pager, bytes, id } } diff --git a/ui/src/components/utilities.rs b/ui/src/components/utilities.rs index 21f5ac897..46bde9e5d 100644 --- a/ui/src/components/utilities.rs +++ b/ui/src/components/utilities.rs @@ -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)] pub enum PageMovement { Up(usize), @@ -295,7 +289,7 @@ pub struct Pager { minimum_width: usize, dirty: bool, - colors: PagerColors, + colors: ThemeAttribute, initialised: bool, content: CellBuffer, movement: Option, @@ -346,7 +340,7 @@ impl Pager { context: Option<&Context>, cursor_pos: Option, mut width: Option, - colors: PagerColors, + colors: ThemeAttribute, ) -> Self { let pager_filter: Option<&String> = if let Some(context) = context { context.settings.pager.filter.as_ref() @@ -435,7 +429,7 @@ impl Pager { text: &str, cursor_pos: Option, width: Option, - colors: PagerColors, + colors: ThemeAttribute, ) -> Self { let lines: Vec = if let Some(width) = width { text.split_lines(width) @@ -477,7 +471,7 @@ impl Pager { ..Default::default() } } - pub fn print_string(content: &mut CellBuffer, lines: Vec, colors: PagerColors) { + pub fn print_string(content: &mut CellBuffer, lines: Vec, colors: ThemeAttribute) { let width = content.size().0; debug!(colors); for (i, l) in lines.iter().enumerate() { diff --git a/ui/src/conf/themes.rs b/ui/src/conf/themes.rs index 75788c3b6..83d02d71e 100644 --- a/ui/src/conf/themes.rs +++ b/ui/src/conf/themes.rs @@ -19,7 +19,7 @@ * along with meli. If not, see . */ -use crate::terminal::Color; +use crate::terminal::{Attr, Color}; use crate::Context; use melib::Result; use serde::{de, Deserialize, Deserializer, Serialize, Serializer}; @@ -27,7 +27,7 @@ use std::borrow::Cow; use std::collections::{HashMap, HashSet}; #[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() { "light" => &context.settings.terminal.themes.light, "dark" | _ => &context.settings.terminal.themes.dark, @@ -35,13 +35,78 @@ pub fn color(context: &Context, key: &'static str) -> Color { 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)] fn unlink<'k, 't: 'k>( - theme: &'t HashMap, ThemeValue>, + theme: &'t HashMap, 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, ThemeAttributeInner>, mut key: &'k Cow<'static, str>, ) -> Color { 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, 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, ThemeAttributeInner>, + mut key: &'k Cow<'static, str>, +) -> Attr { + loop { + match &theme[key].attrs { ThemeValue::Link(ref new_key) => key = new_key, ThemeValue::Value(val) => return *val, } @@ -49,94 +114,116 @@ fn unlink<'k, 't: 'k>( } const DEFAULT_KEYS: &'static [&'static str] = &[ - "general.background", - "general.foreground", - "general.status_bar_fg", - "general.status_bar_bg", - "general.tab_focused_fg", - "general.tab_focused_bg", - "general.tab_unfocused_fg", - "general.tab_unfocused_bg", - "general.tab_bar_bg", - "mail.sidebar_fg", - "mail.sidebar_bg", - "mail.sidebar_unread_count_fg", - "mail.sidebar_unread_count_bg", - "mail.sidebar_index_fg", - "mail.sidebar_index_bg", - "mail.sidebar_highlighted_fg", - "mail.sidebar_highlighted_bg", - "mail.sidebar_highlighted_unread_count_fg", - "mail.sidebar_highlighted_unread_count_bg", - "mail.sidebar_highlighted_index_fg", - "mail.sidebar_highlighted_index_bg", - "mail.sidebar_highlighted_account_fg", - "mail.sidebar_highlighted_account_bg", - "mail.sidebar_highlighted_account_unread_count_fg", - "mail.sidebar_highlighted_account_unread_count_bg", - "mail.sidebar_highlighted_account_index_fg", - "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", + "general", + "general.status_bar", + "general.tab_focused", + "general.tab_unfocused", + "general.tab_bar", + "mail.sidebar", + "mail.sidebar_unread_count", + "mail.sidebar_index", + "mail.sidebar_highlighted", + "mail.sidebar_highlighted_unread_count", + "mail.sidebar_highlighted_index", + "mail.sidebar_highlighted_account", + "mail.sidebar_highlighted_account_unread_count", + "mail.sidebar_highlighted_account_index", + "mail.listing.compact.even", + "mail.listing.compact.odd", + "mail.listing.compact.unseen", + "mail.listing.compact.selected", + "mail.listing.compact.highlighted", + "mail.listing.plain.even", + "mail.listing.plain.odd", + "mail.listing.plain.unseen", + "mail.listing.conversations", + "mail.listing.conversations.subject", + "mail.listing.conversations.from", + "mail.listing.conversations.date", "mail.listing.conversations.padding", - "mail.listing.conversations.unseen_fg", - "mail.listing.conversations.unseen_bg", + "mail.listing.conversations.unseen", "mail.listing.conversations.unseen_padding", - "mail.listing.conversations.highlighted_fg", - "mail.listing.conversations.highlighted_bg", - "mail.listing.conversations.selected_fg", - "mail.listing.conversations.selected_bg", - "mail.view.headers_fg", - "mail.view.headers_bg", - "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", + "mail.listing.conversations.highlighted", + "mail.listing.conversations.selected", + "mail.view.headers", + "mail.view.body", + "mail.listing.attachment_flag", + "mail.listing.thread_snooze_flag", ]; +#[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, + #[serde(default)] + bg: ThemeValue, + #[serde(default)] + attrs: ThemeValue, +} + #[derive(Debug, Clone)] -pub enum ThemeValue { - Value(Color), +pub enum ThemeValue { + Value(T), Link(Cow<'static, str>), } -impl From for ThemeValue { +impl From<&'static str> for ThemeValue { + fn from(from: &'static str) -> Self { + ThemeValue::Link(from.into()) + } +} + +impl From for ThemeValue { fn from(from: Color) -> Self { ThemeValue::Value(from) } } -impl Default for ThemeValue { +impl Default for ThemeValue { fn default() -> Self { ThemeValue::Value(Color::Default) } } -impl<'de> Deserialize<'de> for ThemeValue { +impl Default for ThemeValue { + fn default() -> Self { + ThemeValue::Value(Attr::Default) + } +} + +impl<'de> Deserialize<'de> for ThemeValue { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + if let Ok(s) = ::deserialize(deserializer) { + Ok(ThemeValue::Value(s)) + } else { + Err(de::Error::custom("invalid theme attribute value")) + } + } +} + +impl Serialize for ThemeValue { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: Serializer, + { + match self { + ThemeValue::Value(s) => s.serialize(serializer), + _ => unreachable!(), + } + } +} + +impl<'de> Deserialize<'de> for ThemeValue { fn deserialize(deserializer: D) -> std::result::Result where D: Deserializer<'de>, @@ -156,9 +243,9 @@ impl<'de> Deserialize<'de> for ThemeValue { #[derive(Debug, Clone, Deserialize)] pub struct Theme { #[serde(default)] - pub light: HashMap, ThemeValue>, + pub light: HashMap, ThemeAttributeInner>, #[serde(default)] - pub dark: HashMap, ThemeValue>, + pub dark: HashMap, ThemeAttributeInner>, } impl Theme { @@ -190,236 +277,217 @@ impl Default for Theme { let mut dark = HashMap::default(); macro_rules! add { - ($key:literal, light=$light:literal, dark=$dark:literal) => { - light.insert($key.into(), ThemeValue::Link($light.into())); - dark.insert($key.into(), ThemeValue::Link($dark.into())); - }; - ($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, $($theme:ident={ $($name:ident : $val:expr),*$(,)? }),*$(,)?) => { + $($theme.insert($key.into(), ThemeAttributeInner { + $($name: $val.into()),* + ,..ThemeAttributeInner::default() }));* }; ($key:literal) => { - light.insert($key.into(), ThemeValue::Value(Color::Default)); - dark.insert($key.into(), ThemeValue::Value(Color::Default)); + light.insert($key.into(), ThemeAttributeInner::default()); + dark.insert($key.into(), ThemeAttributeInner::default()); }; } - - add!("general.background"); - add!("general.foreground"); + add!("general"); /* - "general.status_bar_fg", - "general.status_bar_bg", - "general.tab_focused_fg", - "general.tab_focused_bg", - "general.tab_unfocused_fg", - "general.tab_unfocused_bg", - "general.tab_bar_bg", + "general.status_bar", + "general.tab_focused", + "general.tab_unfocused", + "general.tab_bar", */ /* Mail Sidebar */ - add!("mail.sidebar_fg"); - add!("mail.sidebar_bg"); - add!("mail.sidebar_unread_count_fg", dark = Color::Byte(243)); - add!("mail.sidebar_unread_count_bg"); - 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!("mail.sidebar"); + add!("mail.sidebar_unread_count", dark = { fg: Color::Byte(243) }); + add!("mail.sidebar_index", dark = { fg: Color::Byte(243) }); + add!("mail.sidebar_highlighted", dark = { fg: Color::Byte(233), bg: Color::Byte(15) }); add!( - "mail.sidebar_highlighted_unread_count_fg", - light = "mail.sidebar_highlighted_fg", - dark = "mail.sidebar_highlighted_fg" + "mail.sidebar_highlighted_unread_count", + light = { + fg: "mail.sidebar_highlighted", + bg: "mail.sidebar_highlighted" + }, + dark = { + fg: "mail.sidebar_highlighted", + bg: "mail.sidebar_highlighted" + } ); add!( - "mail.sidebar_highlighted_unread_count_bg", - light = "mail.sidebar_highlighted_bg", - dark = "mail.sidebar_highlighted_bg" + "mail.sidebar_highlighted_index", + light = { + fg: "mail.sidebar_index", + bg: "mail.sidebar_highlighted", + }, + dark = { + fg: "mail.sidebar_index", + bg: "mail.sidebar_highlighted", + }, ); add!( - "mail.sidebar_highlighted_index_fg", - light = "mail.sidebar_index_fg", - dark = "mail.sidebar_index_fg" + "mail.sidebar_highlighted_account", + dark = { + fg: Color::Byte(15), + bg: Color::Byte(233), + } ); add!( - "mail.sidebar_highlighted_index_bg", - light = "mail.sidebar_highlighted_bg", - dark = "mail.sidebar_highlighted_bg" + "mail.sidebar_highlighted_account_unread_count", + light = { + fg: "mail.sidebar_unread_count", + bg: "mail.sidebar_highlighted_account", + }, + dark = { + fg: "mail.sidebar_unread_count", + bg: "mail.sidebar_highlighted_account" + } ); add!( - "mail.sidebar_highlighted_account_fg", - dark = Color::Byte(15) - ); - add!( - "mail.sidebar_highlighted_account_bg", - dark = Color::Byte(233) - ); - add!( - "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" + "mail.sidebar_highlighted_account_index", + light = { + fg: "mail.sidebar_index", + bg: "mail.sidebar_highlighted_account" + }, + dark = { + fg: "mail.sidebar_index", + bg: "mail.sidebar_highlighted_account" + } ); /* CompactListing */ - add!("mail.listing.compact.even_fg"); - add!( - "mail.listing.compact.even_bg", - dark = Color::Byte(236), - light = Color::Byte(252) + add!("mail.listing.compact.even", + dark = { + bg: Color::Byte(236) + }, + light = { + bg: Color::Byte(252) + } ); - add!("mail.listing.compact.odd_fg"); - add!("mail.listing.compact.odd_bg"); + add!("mail.listing.compact.odd"); add!( - "mail.listing.compact.unseen_fg", - dark = Color::Byte(0), - light = Color::Byte(0) + "mail.listing.compact.unseen", + dark = { + 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!( - "mail.listing.compact.unseen_bg", - dark = Color::Byte(251), - light = Color::Byte(251) - ); - add!("mail.listing.compact.selected_fg"); - add!( - "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) + "mail.listing.compact.highlighted", + dark = { + bg: Color::Byte(246) + }, + light = { + bg: Color::Byte(244) + } ); /* ConversationsListing */ - add!("mail.listing.conversations.fg"); - add!("mail.listing.conversations.bg"); - add!("mail.listing.conversations.subject_fg"); - add!("mail.listing.conversations.subject_bg"); - 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!("mail.listing.conversations"); + add!("mail.listing.conversations.subject"); + add!("mail.listing.conversations.from"); + add!("mail.listing.conversations.date"); add!( "mail.listing.conversations.padding", - dark = Color::Byte(235), - light = Color::Byte(254) + dark = { + fg: Color::Byte(235), + bg: Color::Byte(235), + }, + light = { + fg: Color::Byte(254), + bg: Color::Byte(254), + } ); add!( "mail.listing.conversations.unseen_padding", - dark = Color::Byte(235), - light = Color::Byte(254) + dark = { + fg: Color::Byte(235), + bg: Color::Byte(235), + }, + light = { + fg: Color::Byte(254), + bg: Color::Byte(254), + } ); add!( - "mail.listing.conversations.unseen_fg", - dark = Color::Byte(0), - light = Color::Byte(0) + "mail.listing.conversations.unseen", + dark = { + fg: Color::Byte(0), + bg: Color::Byte(251) + }, + light = { + fg: Color::Byte(0), + bg: Color::Byte(251) + } ); add!( - "mail.listing.conversations.unseen_bg", - dark = Color::Byte(251), - light = Color::Byte(251) + "mail.listing.conversations.highlighted", + dark = { + bg: Color::Byte(246), + }, + light = { + bg: Color::Byte(246) + } ); - add!("mail.listing.conversations.highlighted_fg"); - add!( - "mail.listing.conversations.highlighted_bg", - dark = Color::Byte(246), - light = Color::Byte(246) - ); - add!("mail.listing.conversations.selected_fg"); - add!( - "mail.listing.conversations.selected_bg", - dark = Color::Byte(210), - light = Color::Byte(210) + add!("mail.listing.conversations.selected", + dark = { + bg: Color::Byte(210), + }, + light = { + bg: Color::Byte(210) + } ); /* - "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.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.plain.even", + "mail.listing.plain.odd", + "mail.listing.plain.unseen", + "mail.listing.conversations.subject", + "mail.listing.conversations.from", + "mail.listing.conversations.date", "mail.listing.conversations.unseen_padding", */ add!( - "mail.view.headers_fg", - dark = Color::Byte(33), - light = Color::Black + "mail.view.headers", + dark = { + fg: Color::Byte(33), + }, + light = { + fg: Color::Black, + } ); - add!("mail.view.headers_bg"); - add!("mail.view.body_fg"); - add!("mail.view.body_bg"); + add!("mail.view.body"); add!( - "mail.listing.attachment_flag_fg", - light = Color::Byte(103), - dark = Color::Byte(103) + "mail.listing.attachment_flag", + light = { + fg: Color::Byte(103), + }, + dark = { + fg: Color::Byte(103) + } ); add!( - "mail.listing.attachment_flag_bg", - light = Color::Default, - dark = Color::Default - ); - - add!( - "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 + "mail.listing.thread_snooze_flag", + light = { + fg: Color::Red, + }, + dark = { + fg: Color::Red, + } ); Theme { light, dark } @@ -431,21 +499,35 @@ impl Serialize for Theme { where S: Serializer, { - let mut dark: HashMap, Color> = Default::default(); - let mut light: HashMap, Color> = Default::default(); + let mut dark: HashMap, ThemeAttribute> = Default::default(); + let mut light: HashMap, ThemeAttribute> = Default::default(); 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() { - 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)] struct ThemeSer { - light: HashMap, Color>, - dark: HashMap, Color>, + light: HashMap, ThemeAttribute>, + dark: HashMap, ThemeAttribute>, } use serde::ser::SerializeStruct; let mut s = serializer.serialize_struct("ThemeSer", 2)?; diff --git a/ui/src/terminal/cells.rs b/ui/src/terminal/cells.rs index 72df26072..58b5c8066 100644 --- a/ui/src/terminal/cells.rs +++ b/ui/src/terminal/cells.rs @@ -1385,6 +1385,53 @@ pub enum Attr { BoldReverseUnderline = 0b111, } +impl Default for Attr { + fn default() -> Self { + Attr::Default + } +} + +impl<'de> Deserialize<'de> for Attr { + fn deserialize(deserializer: D) -> std::result::Result + where + D: Deserializer<'de>, + { + if let Ok(s) = ::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(&self, serializer: S) -> std::result::Result + 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( grid_dest: &mut CellBuffer, grid_src: &CellBuffer,