ui: add theming support

Configuration flag "terminal.themes" has two default theme entries,
"dark" and "light".

This commit alters only CompactListing for theme support.
async
Manos Pitsidianakis 2020-01-19 14:43:36 +02:00
parent 63ff25b36a
commit a9842cacee
Signed by: Manos Pitsidianakis
GPG Key ID: 73627C2F690DF710
5 changed files with 325 additions and 78 deletions

View File

@ -173,3 +173,10 @@ impl From<&str> for MeliError {
MeliError::new(kind.to_string())
}
}
impl From<String> for MeliError {
#[inline]
fn from(kind: String) -> MeliError {
MeliError::new(kind)
}
}

View File

@ -126,34 +126,24 @@ impl ListingTrait for CompactListing {
let thread = threads.thread_ref(thread_hash);
let fg_color = if thread.unseen() > 0 {
Color::Byte(0)
crate::conf::color(context, "mail.listing.compact.unseen_fg")
} else if self.cursor_pos.2 == idx {
crate::conf::color(context, "mail.listing.compact.highlighted_fg")
} else if idx % 2 == 0 {
crate::conf::color(context, "mail.listing.compact.even_fg")
} else {
Color::Default
crate::conf::color(context, "mail.listing.compact.odd_fg")
};
let bg_color = if context.settings.terminal.theme == "light" {
if self.cursor_pos.2 == idx {
Color::Byte(244)
} else if self.selection[&thread_hash] {
Color::Byte(210)
} else if thread.unseen() > 0 {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(252)
} else {
Color::Default
}
let bg_color = if self.cursor_pos.2 == idx {
crate::conf::color(context, "mail.listing.compact.highlighted_bg")
} else if self.selection[&thread_hash] {
crate::conf::color(context, "mail.listing.compact.selected_bg")
} else if thread.unseen() > 0 {
crate::conf::color(context, "mail.listing.compact.unseen_bg")
} else if idx % 2 == 0 {
crate::conf::color(context, "mail.listing.compact.even_bg")
} else {
if self.cursor_pos.2 == idx {
Color::Byte(246)
} else if self.selection[&thread_hash] {
Color::Byte(210)
} else if thread.unseen() > 0 {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
}
crate::conf::color(context, "mail.listing.compact.odd_bg")
};
let (upper_left, bottom_right) = area;
@ -356,7 +346,10 @@ impl ListingTrait for CompactListing {
let c = &self.data_columns.columns[0][(0, r + top_idx)];
if self.selection[&thread_hash] {
(c.fg(), Color::Byte(210))
(
c.fg(),
crate::conf::color(context, "mail.listing.compact.selected_bg"),
)
} else {
(c.fg(), c.bg())
}
@ -810,27 +803,21 @@ impl CompactListing {
panic!();
}
let thread = threads.thread_ref(thread);
let fg_color = if thread.unseen() > 0 {
Color::Byte(0)
let (fg_color, bg_color) = if thread.unseen() > 0 {
(
crate::conf::color(context, "mail.listing.compact.unseen_fg"),
crate::conf::color(context, "mail.listing.compact.unseen_bg"),
)
} else if idx % 2 == 0 {
(
crate::conf::color(context, "mail.listing.compact.even_fg"),
crate::conf::color(context, "mail.listing.compact.even_bg"),
)
} else {
Color::Default
};
let bg_color = if context.settings.terminal.theme == "light" {
if thread.unseen() > 0 {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(252)
} else {
Color::Default
}
} else {
if thread.unseen() > 0 {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
}
(
crate::conf::color(context, "mail.listing.compact.odd_fg"),
crate::conf::color(context, "mail.listing.compact.odd_bg"),
)
};
let (x, _) = write_string_to_grid(
&idx.to_string(),
@ -921,14 +908,26 @@ impl CompactListing {
}
match (thread.snoozed(), thread.has_attachments()) {
(true, true) => {
self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103));
self.data_columns.columns[3][(2, idx)].set_fg(Color::Red);
self.data_columns.columns[3][(0, idx)].set_fg(crate::conf::color(
context,
"mail.listing.attachment_flag_fg",
));
self.data_columns.columns[3][(2, idx)].set_fg(crate::conf::color(
context,
"mail.listing.thread_snooze_flag_fg",
));
}
(true, false) => {
self.data_columns.columns[3][(0, idx)].set_fg(Color::Red);
self.data_columns.columns[3][(0, idx)].set_fg(crate::conf::color(
context,
"mail.listing.thread_snooze_flag_fg",
));
}
(false, true) => {
self.data_columns.columns[3][(0, idx)].set_fg(Color::Byte(103));
self.data_columns.columns[3][(0, idx)].set_fg(crate::conf::color(
context,
"mail.listing.attachment_flag_fg",
));
}
(false, false) => {}
}
@ -981,30 +980,24 @@ impl CompactListing {
* arrive */
return;
}
let envelope: EnvelopeRef = account.collection.get_env(env_hash);
let fg_color = if thread.unseen() > 0 {
Color::Byte(0)
} else {
Color::Default
};
let idx = self.order[&thread_hash];
let bg_color = if context.settings.terminal.theme == "light" {
if thread.unseen() > 0 {
Color::Byte(251)
} else if idx % 2 == 0 {
Color::Byte(252)
} else {
Color::Default
}
let (fg_color, bg_color) = if thread.unseen() > 0 {
(
crate::conf::color(context, "mail.listing.compact.unseen_fg"),
crate::conf::color(context, "mail.listing.compact.unseen_bg"),
)
} else if idx % 2 == 0 {
(
crate::conf::color(context, "mail.listing.compact.even_fg"),
crate::conf::color(context, "mail.listing.compact.even_bg"),
)
} else {
if thread.unseen() > 0 {
Color::Byte(253)
} else if idx % 2 == 0 {
Color::Byte(236)
} else {
Color::Default
}
(
crate::conf::color(context, "mail.listing.compact.odd_fg"),
crate::conf::color(context, "mail.listing.compact.odd_bg"),
)
};
let envelope: EnvelopeRef = account.collection.get_env(env_hash);
let strings = self.make_entry_string(&envelope, context, threads, thread_hash);
drop(envelope);
let columns = &mut self.data_columns.columns;
@ -1108,14 +1101,26 @@ impl CompactListing {
}
match (thread.snoozed(), thread.has_attachments()) {
(true, true) => {
columns[3][(0, idx)].set_fg(Color::Byte(103));
columns[3][(2, idx)].set_fg(Color::Red);
columns[3][(0, idx)].set_fg(crate::conf::color(
context,
"mail.listing.attachment_flag_fg",
));
columns[3][(2, idx)].set_fg(crate::conf::color(
context,
"mail.listing.thread_snooze_flag_fg",
));
}
(true, false) => {
columns[3][(0, idx)].set_fg(Color::Red);
columns[3][(0, idx)].set_fg(crate::conf::color(
context,
"mail.listing.thread_snooze_flag_fg",
));
}
(false, true) => {
columns[3][(0, idx)].set_fg(Color::Byte(103));
columns[3][(0, idx)].set_fg(crate::conf::color(
context,
"mail.listing.attachment_flag_fg",
));
}
(false, false) => {}
}

View File

@ -33,6 +33,8 @@ pub mod tags;
pub mod shortcuts;
mod listing;
pub mod terminal;
mod themes;
pub use themes::*;
pub mod accounts;
pub use self::accounts::Account;
@ -77,6 +79,7 @@ pub struct MailUIConf {
pub identity: Option<String>,
pub index_style: Option<IndexStyle>,
pub tags: Option<TagsSettings>,
pub theme: Option<Theme>,
}
#[serde(default)]
@ -330,9 +333,26 @@ impl FileSettings {
}
}
FileSettings::validate(config_path.to_str().unwrap())?;
let s = pp::pp(config_path.to_str().unwrap()).unwrap();
let s: FileSettings = toml::from_str(&s).unwrap();
let path = config_path
.to_str()
.expect("Configuration file path was not valid UTF-8");
FileSettings::validate(path)?;
let mut s: FileSettings = toml::from_str(&pp::pp(path)?).map_err(|err| err.to_string())?;
let Theme {
light: default_light,
dark: default_dark,
} = Theme::default();
for (k, v) in default_light.into_iter() {
if !s.terminal.themes.light.contains_key(&k) {
s.terminal.themes.light.insert(k, v);
}
}
for (k, v) in default_dark.into_iter() {
if !s.terminal.themes.dark.contains_key(&k) {
s.terminal.themes.dark.insert(k, v);
}
}
Ok(s)
}

View File

@ -20,6 +20,7 @@
*/
use super::deserializers::non_empty_string;
use super::Theme;
/// Settings for terminal display
#[derive(Debug, Deserialize, Clone, Serialize)]
@ -27,6 +28,7 @@ use super::deserializers::non_empty_string;
pub struct TerminalSettings {
/// light, dark
pub theme: String,
pub themes: Theme,
pub ascii_drawing: bool,
#[serde(deserialize_with = "non_empty_string")]
pub window_title: Option<String>,
@ -36,6 +38,7 @@ impl Default for TerminalSettings {
fn default() -> Self {
TerminalSettings {
theme: "dark".to_string(),
themes: Theme::default(),
ascii_drawing: false,
window_title: Some("meli".to_string()),
}

View File

@ -0,0 +1,212 @@
/*
* meli - themes conf module
*
* Copyright 2019 Manos Pitsidianakis
*
* This file is part of meli.
*
* meli is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* meli is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with meli. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::terminal::Color;
use crate::Context;
use std::borrow::Cow;
use std::collections::{HashMap, HashSet};
#[inline(always)]
pub fn color(context: &Context, key: &'static str) -> Color {
(match context.settings.terminal.theme.as_str() {
"light" => &context.settings.terminal.themes.light,
"dark" | _ => &context.settings.terminal.themes.dark,
})[key]
}
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.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_fg",
"mail.listing.compact.selected_fg",
"mail.listing.compact.selected_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.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.unseen_fg",
"mail.listing.conversations.unseen_bg",
"mail.listing.conversations.unseen_padding",
"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",
];
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Theme {
#[serde(default)]
pub light: HashMap<Cow<'static, str>, Color>,
#[serde(default)]
pub dark: HashMap<Cow<'static, str>, Color>,
}
impl Theme {
pub fn validate(&self) -> Option<String> {
let hash_set: HashSet<&'static str> = DEFAULT_KEYS.into_iter().map(|k| *k).collect();
let keys: Vec<&'_ str> = self
.light
.keys()
.chain(self.dark.keys())
.filter_map(|k| {
if !hash_set.contains(&k.as_ref()) {
Some(k.as_ref())
} else {
None
}
})
.collect();
if keys.is_empty() {
None
} else {
Some(format!("Unrecognized theme keywords: {}", keys.join(", ")))
}
}
}
impl Default for Theme {
fn default() -> Theme {
let mut light = HashMap::default();
let mut dark = HashMap::default();
light.insert("general.background".into(), Color::Default);
light.insert("general.foreground".into(), Color::Default);
dark.insert("general.background".into(), Color::Default);
dark.insert("general.foreground".into(), Color::Default);
/*
"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",
*/
light.insert("mail.listing.compact.even_fg".into(), Color::Default);
light.insert("mail.listing.compact.even_bg".into(), Color::Byte(252));
light.insert("mail.listing.compact.odd_fg".into(), Color::Default);
light.insert("mail.listing.compact.odd_bg".into(), Color::Default);
light.insert("mail.listing.compact.unseen_fg".into(), Color::Byte(0));
light.insert("mail.listing.compact.unseen_bg".into(), Color::Byte(251));
light.insert("mail.listing.compact.selected_fg".into(), Color::Default);
light.insert("mail.listing.compact.selected_bg".into(), Color::Byte(210));
light.insert("mail.listing.compact.highlighted_fg".into(), Color::Default);
light.insert(
"mail.listing.compact.highlighted_bg".into(),
Color::Byte(244),
);
dark.insert("mail.listing.compact.even_fg".into(), Color::Default);
dark.insert("mail.listing.compact.even_bg".into(), Color::Byte(236));
dark.insert("mail.listing.compact.odd_fg".into(), Color::Default);
dark.insert("mail.listing.compact.odd_bg".into(), Color::Default);
dark.insert("mail.listing.compact.unseen_fg".into(), Color::Byte(0));
dark.insert("mail.listing.compact.unseen_bg".into(), Color::Byte(251));
dark.insert("mail.listing.compact.selected_fg".into(), Color::Default);
dark.insert("mail.listing.compact.selected_bg".into(), Color::Byte(210));
dark.insert("mail.listing.compact.highlighted_fg".into(), Color::Default);
dark.insert(
"mail.listing.compact.highlighted_bg".into(),
Color::Byte(246),
);
/*
"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.conversations.padding",
"mail.listing.conversations.unseen_fg",
"mail.listing.conversations.unseen_bg",
"mail.listing.conversations.unseen_padding",
"mail.view.headers_fg",
"mail.view.headers_bg",
"mail.view.body_fg",
"mail.view.body_bg",
*/
light.insert("mail.listing.attachment_flag_fg".into(), Color::Byte(103));
light.insert("mail.listing.attachment_flag_bg".into(), Color::Default);
light.insert("mail.listing.thread_snooze_flag_fg".into(), Color::Red);
light.insert("mail.listing.thread_snooze_flag_bg".into(), Color::Default);
dark.insert("mail.listing.attachment_flag_fg".into(), Color::Byte(103));
dark.insert("mail.listing.attachment_flag_bg".into(), Color::Default);
dark.insert("mail.listing.thread_snooze_flag_fg".into(), Color::Red);
dark.insert("mail.listing.thread_snooze_flag_bg".into(), Color::Default);
Theme { light, dark }
}
}