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
parent
63ff25b36a
commit
a9842cacee
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) => {}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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()),
|
||||
}
|
||||
|
|
|
@ -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 }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue