Skip to content

Commit

Permalink
Merge pull request #47 from l4l/hex-color
Browse files Browse the repository at this point in the history
Support specifying colors with css-like hex values
  • Loading branch information
l4l authored May 16, 2021
2 parents 6f8a9fa + 9a8059f commit dbb9e7f
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Features

- Specify colors in css-like hex (#47)
- Fallback to input at dialog overflow (#43)
- Support environments without layer-shell protocol (#42)

Expand Down
83 changes: 75 additions & 8 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::convert::TryInto;
use std::ffi::CString;
use std::path::PathBuf;

Expand All @@ -9,6 +10,70 @@ const DEFAULT_CONFIG_PATH: &str = concat!(crate::prog_name!(), ".config");

mod params;

#[derive(Deserialize, Debug, Clone, Copy)]
pub struct Color(#[serde(deserialize_with = "deserialize_color")] u32);

impl std::ops::Deref for Color {
type Target = u32;

fn deref(&self) -> &Self::Target {
&self.0
}
}

fn deserialize_color<'de, D: serde::Deserializer<'de>>(d: D) -> Result<u32, D::Error> {
struct ColorDeHelper;

impl<'de> serde::de::Visitor<'de> for ColorDeHelper {
type Value = u32;

fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
formatter,
"invalid color value, must be either numerical or css-like hex value with # prefix"
)
}

fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
value.try_into().map_err(serde::de::Error::custom)
}

fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
let part = match value.chars().next() {
None => return Err(serde::de::Error::custom("color cannot be empty")),
Some('#') => value.split_at(1).1,
Some(_) => {
return Err(serde::de::Error::custom(
"color can be either decimal or hex number prefixed with '#'",
))
}
};

let decoded = u32::from_str_radix(part, 16).map_err(serde::de::Error::custom);
match part.len() {
3 => {
let decoded = decoded?;
let (r, g, b) = ((decoded & 0xf00) >> 8, (decoded & 0xf0) >> 4, decoded & 0xf);
Ok((r << 4 | r) << 24 | (g << 4 | g) << 16 | (b << 4 | b) << 8 | 0xff)
}
6 => decoded.map(|d| d << 8 | 0xff),
8 => decoded,
_ => Err(serde::de::Error::custom(
"hex color can only be specified in #RGB, #RRGGBB, or #RRGGBBAA format",
)),
}
}
}

d.deserialize_any(ColorDeHelper)
}

#[derive(Default, Deserialize)]
pub struct Config {
width: Option<u32>,
Expand All @@ -19,8 +84,8 @@ pub struct Config {
term: Option<String>,
font: Option<String>,
font_size: Option<u16>,
bg_color: Option<u32>,
font_color: Option<u32>,
bg_color: Option<Color>,
font_color: Option<Color>,

icon: Option<Icon>,

Expand All @@ -38,8 +103,8 @@ impl Config {
struct InputText {
font: Option<String>,
font_size: Option<u16>,
bg_color: Option<u32>,
font_color: Option<u32>,
bg_color: Option<Color>,
font_color: Option<Color>,
margin: Option<Margin>,
padding: Option<Padding>,
}
Expand All @@ -48,9 +113,9 @@ struct InputText {
struct ListItems {
font: Option<String>,
font_size: Option<u16>,
font_color: Option<u32>,
selected_font_color: Option<u32>,
match_color: Option<u32>,
font_color: Option<Color>,
selected_font_color: Option<Color>,
match_color: Option<Color>,
margin: Option<Margin>,
item_spacing: Option<f32>,
icon_spacing: Option<f32>,
Expand All @@ -73,7 +138,9 @@ fn config_path() -> PathBuf {
impl Config {
pub fn load(path: Option<PathBuf>) -> Self {
std::fs::read_to_string(path.unwrap_or_else(config_path))
.map(|config_content| toml::from_str(&config_content).expect("invalid config"))
.map(|config_content| {
toml::from_str(&config_content).unwrap_or_else(|e| panic!("Invalid config: {}", e))
})
.unwrap_or_default()
}

Expand Down
16 changes: 8 additions & 8 deletions src/config/params.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use font_kit::properties::Properties;
use font_kit::source::SystemSource;
use raqote::SolidSource;

use super::Config;
use super::{Color, Config};
use crate::desktop::{IconConfig, DEFAULT_THEME};
use crate::draw::{BgParams, InputTextParams, ListParams};
use crate::icon::Icon;
Expand All @@ -31,10 +31,10 @@ impl<'a> From<&'a Config> for InputTextParams {
.unwrap_or_else(default_font),
font_size: select_conf!(config, input_text, font_size).unwrap_or(DEFAULT_FONT_SIZE),
bg_color: select_conf!(config, input_text, bg_color)
.map(u32_to_solid_source)
.map(color_to_solid_source)
.unwrap_or_else(|| SolidSource::from_unpremultiplied_argb(0xc0, 0x75, 0x71, 0x5e)),
font_color: select_conf!(config, input_text, font_color)
.map(u32_to_solid_source)
.map(color_to_solid_source)
.unwrap_or_else(default_font_color),
margin: select_conf!(noglob: config, input_text, margin)
.unwrap_or_else(|| Margin::all(5.0)),
Expand All @@ -52,13 +52,13 @@ impl<'a> From<&'a Config> for ListParams {
.unwrap_or_else(default_font),
font_size: select_conf!(config, list_items, font_size).unwrap_or(DEFAULT_FONT_SIZE),
font_color: select_conf!(config, list_items, font_color)
.map(u32_to_solid_source)
.map(color_to_solid_source)
.unwrap_or_else(default_font_color),
selected_font_color: select_conf!(noglob: config, list_items, selected_font_color)
.map(u32_to_solid_source)
.map(color_to_solid_source)
.unwrap_or_else(|| SolidSource::from_unpremultiplied_argb(0xff, 0xa6, 0xe2, 0x2e)),
match_color: select_conf!(noglob: config, list_items, match_color)
.map(u32_to_solid_source),
.map(color_to_solid_source),
icon_size: config
.icon
.as_ref()
Expand All @@ -81,7 +81,7 @@ impl<'a> From<&'a Config> for BgParams {
BgParams {
color: config
.bg_color
.map(u32_to_solid_source)
.map(color_to_solid_source)
.unwrap_or_else(|| SolidSource::from_unpremultiplied_argb(0xee, 0x27, 0x28, 0x22)),
}
}
Expand Down Expand Up @@ -136,7 +136,7 @@ fn font_by_name(name: String) -> Font {
.unwrap()
}

fn u32_to_solid_source(x: u32) -> SolidSource {
fn color_to_solid_source(x: Color) -> SolidSource {
let bytes = x.to_be_bytes();
SolidSource::from_unpremultiplied_argb(bytes[3], bytes[0], bytes[1], bytes[2])
}

0 comments on commit dbb9e7f

Please sign in to comment.