ui/themes: load other themes from ./themes/ dir
parent
ab0b4f5168
commit
5230ce2d03
103
ui/src/conf.rs
103
ui/src/conf.rs
|
@ -289,14 +289,14 @@ pub struct Settings {
|
|||
|
||||
impl FileSettings {
|
||||
pub fn new() -> Result<FileSettings> {
|
||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli");
|
||||
let config_path = match env::var("MELI_CONFIG") {
|
||||
Ok(path) => PathBuf::from(path),
|
||||
Err(_) => {
|
||||
let xdg_dirs = xdg::BaseDirectories::with_prefix("meli").unwrap();
|
||||
xdg_dirs
|
||||
.place_config_file("config")
|
||||
.expect("cannot create configuration directory")
|
||||
}
|
||||
Err(_) => xdg_dirs
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.place_config_file("config")
|
||||
.expect("cannot create configuration directory"),
|
||||
};
|
||||
if !config_path.exists() {
|
||||
println!(
|
||||
|
@ -341,12 +341,54 @@ impl FileSettings {
|
|||
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 theme in s.terminal.themes.other_themes.values_mut() {
|
||||
for (k, v) in default_dark.clone().into_iter() {
|
||||
if !theme.contains_key(&k) {
|
||||
theme.insert(k, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Ok(xdg_dirs) = xdg_dirs {
|
||||
for theme_folder in xdg_dirs.find_config_files("themes") {
|
||||
let read_dir = std::fs::read_dir(theme_folder)?;
|
||||
for theme in read_dir {
|
||||
let theme = theme?;
|
||||
if theme.path().is_file() {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
let theme_name = if let Some(n) = theme
|
||||
.path()
|
||||
.file_stem()
|
||||
.map(|f| String::from_utf8_lossy(f.as_bytes()).into_owned())
|
||||
{
|
||||
n
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let mut t: HashMap<std::borrow::Cow<'static, str>, ThemeAttributeInner> =
|
||||
toml::from_str(&pp::pp(theme.path())?).map_err(|err| {
|
||||
format!(
|
||||
"Could not parse theme in `{}`: {}",
|
||||
theme.path().display(),
|
||||
err.to_string()
|
||||
)
|
||||
})?;
|
||||
for (k, v) in default_dark.clone().into_iter() {
|
||||
if !t.contains_key(&k) {
|
||||
t.insert(k, v);
|
||||
}
|
||||
}
|
||||
s.terminal.themes.other_themes.insert(theme_name, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (k, v) in default_dark.into_iter() {
|
||||
if !s.terminal.themes.dark.contains_key(&k) {
|
||||
s.terminal.themes.dark.insert(k, v);
|
||||
|
@ -359,7 +401,7 @@ impl FileSettings {
|
|||
|
||||
pub fn validate(path: &str) -> Result<()> {
|
||||
let s = pp::pp(path)?;
|
||||
let s: FileSettings = toml::from_str(&s).map_err(|e| {
|
||||
let mut s: FileSettings = toml::from_str(&s).map_err(|e| {
|
||||
MeliError::new(format!(
|
||||
"{}:\nConfig file contains errors: {}",
|
||||
path,
|
||||
|
@ -377,6 +419,44 @@ impl FileSettings {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(xdg_dirs) = xdg::BaseDirectories::with_prefix("meli") {
|
||||
for theme_folder in xdg_dirs.find_config_files("themes") {
|
||||
let read_dir = std::fs::read_dir(theme_folder)?;
|
||||
for theme in read_dir {
|
||||
let theme = theme?;
|
||||
if theme.path().is_file() {
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
let theme_name = if let Some(n) = theme
|
||||
.path()
|
||||
.file_stem()
|
||||
.map(|f| String::from_utf8_lossy(f.as_bytes()).into_owned())
|
||||
{
|
||||
n
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let t: HashMap<std::borrow::Cow<'static, str>, ThemeAttributeInner> =
|
||||
toml::from_str(&pp::pp(theme.path())?).map_err(|err| {
|
||||
format!(
|
||||
"Could not parse theme in `{}`: {}",
|
||||
theme.path().display(),
|
||||
err.to_string()
|
||||
)
|
||||
})?;
|
||||
s.terminal.themes.other_themes.insert(theme_name, t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
match s.terminal.theme.as_str() {
|
||||
"dark" | "light" => {}
|
||||
t if s.terminal.themes.other_themes.contains_key(t) => {}
|
||||
t => {
|
||||
return Err(MeliError::new(format!("Theme `{}` was not found.", t)));
|
||||
}
|
||||
}
|
||||
|
||||
for (name, acc) in s.accounts {
|
||||
let FileAccount {
|
||||
root_folder,
|
||||
|
@ -745,12 +825,11 @@ mod pp {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn pp(path: &str) -> Result<String> {
|
||||
let p = &Path::new(path);
|
||||
let p_buf: PathBuf = if p.is_relative() {
|
||||
p.canonicalize()?
|
||||
pub fn pp<P: AsRef<Path>>(path: P) -> Result<String> {
|
||||
let p_buf: PathBuf = if path.as_ref().is_relative() {
|
||||
path.as_ref().canonicalize()?
|
||||
} else {
|
||||
p.to_path_buf()
|
||||
path.as_ref().to_path_buf()
|
||||
};
|
||||
|
||||
let ret = pp_helper(&p_buf, 0);
|
||||
|
|
|
@ -251,7 +251,7 @@ impl<T: Serialize> Serialize for ThemeValue<T> {
|
|||
{
|
||||
match self {
|
||||
ThemeValue::Value(s) => s.serialize(serializer),
|
||||
_ => unreachable!(),
|
||||
ThemeValue::Link(s) => serializer.serialize_str(s.as_ref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -560,14 +560,15 @@ impl Serialize for Theme {
|
|||
{
|
||||
let mut dark: HashMap<Cow<'static, str>, ThemeAttribute> = Default::default();
|
||||
let mut light: HashMap<Cow<'static, str>, ThemeAttribute> = Default::default();
|
||||
let mut other_themes: HashMap<String, _> = Default::default();
|
||||
|
||||
for k in self.dark.keys() {
|
||||
dark.insert(
|
||||
k.clone(),
|
||||
ThemeAttribute {
|
||||
fg: unlink_fg(&self.light, k),
|
||||
bg: unlink_bg(&self.light, k),
|
||||
attrs: unlink_attrs(&self.light, k),
|
||||
fg: unlink_fg(&self.dark, k),
|
||||
bg: unlink_bg(&self.dark, k),
|
||||
attrs: unlink_attrs(&self.dark, k),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
@ -583,16 +584,25 @@ impl Serialize for Theme {
|
|||
);
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct ThemeSer {
|
||||
light: HashMap<Cow<'static, str>, ThemeAttribute>,
|
||||
dark: HashMap<Cow<'static, str>, ThemeAttribute>,
|
||||
for (name, t) in self.other_themes.iter() {
|
||||
let mut new_map: HashMap<Cow<'static, str>, ThemeAttribute> = Default::default();
|
||||
|
||||
for k in t.keys() {
|
||||
new_map.insert(
|
||||
k.clone(),
|
||||
ThemeAttribute {
|
||||
fg: unlink_fg(&t, k),
|
||||
bg: unlink_bg(&t, k),
|
||||
attrs: unlink_attrs(&t, k),
|
||||
},
|
||||
);
|
||||
}
|
||||
other_themes.insert(name.to_string(), new_map);
|
||||
}
|
||||
use serde::ser::SerializeStruct;
|
||||
let mut s = serializer.serialize_struct("ThemeSer", 2)?;
|
||||
s.serialize_field("light", &light)?;
|
||||
s.serialize_field("dark", &dark)?;
|
||||
s.end()
|
||||
|
||||
other_themes.insert("light".to_string(), light);
|
||||
other_themes.insert("dark".to_string(), dark);
|
||||
other_themes.serialize(serializer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1098,8 +1098,8 @@ impl Serialize for Color {
|
|||
Color::Green | Color::Byte(2) => serializer.serialize_str("Green"),
|
||||
Color::Byte(3) => serializer.serialize_str("Olive"),
|
||||
Color::Byte(4) => serializer.serialize_str("Navy"),
|
||||
Color::Byte(5) => serializer.serialize_str("Purple"),
|
||||
Color::Byte(6) => serializer.serialize_str("Teal"),
|
||||
Color::Byte(5) | Color::Magenta => serializer.serialize_str("Purple"),
|
||||
Color::Byte(6) | Color::Cyan => serializer.serialize_str("Teal"),
|
||||
Color::Byte(7) => serializer.serialize_str("Silver"),
|
||||
Color::Byte(8) => serializer.serialize_str("Grey"),
|
||||
Color::Red | Color::Byte(9) => serializer.serialize_str("Red"),
|
||||
|
@ -1349,7 +1349,10 @@ impl Serialize for Color {
|
|||
Color::Byte(253) => serializer.serialize_str("Grey85"),
|
||||
Color::Byte(254) => serializer.serialize_str("Grey89"),
|
||||
Color::Byte(255) => serializer.serialize_str("Grey93"),
|
||||
_ => serializer.serialize_str("Black"), // FIXME
|
||||
Color::Rgb(r, g, b) => {
|
||||
serializer.serialize_str(&format!("#{:02x}{:02x}{:02x}", r, g, b))
|
||||
}
|
||||
Color::Default => serializer.serialize_str("Default"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue