Color Schemes (#142)
* WIP: Color Schemes * Import/export using color_schemes config * Finish color scheme implementation * Add color scheme rename
This commit is contained in:
parent
01052fae0b
commit
89e1dcb83a
28 changed files with 2630 additions and 861 deletions
157
src/config.rs
157
src/config.rs
|
|
@ -5,6 +5,7 @@ use cosmic::{
|
|||
theme,
|
||||
};
|
||||
use cosmic_text::{Metrics, Stretch, Weight};
|
||||
use hex_color::HexColor;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
|
|
@ -31,6 +32,139 @@ impl AppTheme {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ColorSchemeId(pub u64);
|
||||
|
||||
//TODO: there is a lot of extra code to keep the exported color scheme clean,
|
||||
//consider how to reduce this
|
||||
fn de_color_opt<'de, D>(deserializer: D) -> Result<Option<HexColor>, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
let hex_color: HexColor = Deserialize::deserialize(deserializer)?;
|
||||
Ok(Some(hex_color))
|
||||
}
|
||||
|
||||
fn ser_color_opt<S>(hex_color_opt: &Option<HexColor>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serde::Serializer,
|
||||
{
|
||||
use serde::ser::Error as _;
|
||||
match hex_color_opt {
|
||||
Some(hex_color) => Serialize::serialize(hex_color, serializer),
|
||||
None => Err(S::Error::custom("ser_color_opt called with None")),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct ColorSchemeAnsi {
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub black: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub red: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub green: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub yellow: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub blue: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub magenta: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub cyan: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub white: Option<HexColor>,
|
||||
}
|
||||
|
||||
impl ColorSchemeAnsi {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.black.is_none()
|
||||
&& self.red.is_none()
|
||||
&& self.green.is_none()
|
||||
&& self.yellow.is_none()
|
||||
&& self.blue.is_none()
|
||||
&& self.magenta.is_none()
|
||||
&& self.cyan.is_none()
|
||||
&& self.white.is_none()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
|
||||
#[serde(default, deny_unknown_fields)]
|
||||
pub struct ColorScheme {
|
||||
pub name: String,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub foreground: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub background: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub cursor: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub bright_foreground: Option<HexColor>,
|
||||
#[serde(
|
||||
deserialize_with = "de_color_opt",
|
||||
serialize_with = "ser_color_opt",
|
||||
skip_serializing_if = "Option::is_none"
|
||||
)]
|
||||
pub dim_foreground: Option<HexColor>,
|
||||
#[serde(skip_serializing_if = "ColorSchemeAnsi::is_empty")]
|
||||
pub normal: ColorSchemeAnsi,
|
||||
#[serde(skip_serializing_if = "ColorSchemeAnsi::is_empty")]
|
||||
pub bright: ColorSchemeAnsi,
|
||||
#[serde(skip_serializing_if = "ColorSchemeAnsi::is_empty")]
|
||||
pub dim: ColorSchemeAnsi,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ProfileId(pub u64);
|
||||
|
|
@ -63,6 +197,7 @@ impl Default for Profile {
|
|||
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct Config {
|
||||
pub app_theme: AppTheme,
|
||||
pub color_schemes: BTreeMap<ColorSchemeId, ColorScheme>,
|
||||
pub font_name: String,
|
||||
pub font_size: u16,
|
||||
pub font_weight: u16,
|
||||
|
|
@ -84,6 +219,7 @@ impl Default for Config {
|
|||
Self {
|
||||
app_theme: AppTheme::System,
|
||||
bold_font_weight: Weight::BOLD.0,
|
||||
color_schemes: BTreeMap::new(),
|
||||
dim_font_weight: Weight::NORMAL.0,
|
||||
focus_follow_mouse: false,
|
||||
font_name: "Fira Mono".to_string(),
|
||||
|
|
@ -102,6 +238,25 @@ impl Default for Config {
|
|||
}
|
||||
|
||||
impl Config {
|
||||
// Get a sorted and adjusted for duplicates list of color scheme names and ids
|
||||
pub fn color_scheme_names(&self) -> Vec<(String, ColorSchemeId)> {
|
||||
let mut color_scheme_names =
|
||||
Vec::<(String, ColorSchemeId)>::with_capacity(self.color_schemes.len());
|
||||
for (color_scheme_id, color_scheme) in self.color_schemes.iter() {
|
||||
let mut name = color_scheme.name.clone();
|
||||
|
||||
let mut copies = 1;
|
||||
while color_scheme_names.iter().find(|x| x.0 == name).is_some() {
|
||||
copies += 1;
|
||||
name = format!("{} ({})", color_scheme.name, copies);
|
||||
}
|
||||
|
||||
color_scheme_names.push((name, *color_scheme_id));
|
||||
}
|
||||
color_scheme_names.sort_by(|a, b| lexical_sort::natural_lexical_cmp(&a.0, &b.0));
|
||||
color_scheme_names
|
||||
}
|
||||
|
||||
fn font_size_adjusted(&self, zoom_adj: i8) -> f32 {
|
||||
let font_size = f32::from(self.font_size).max(1.0);
|
||||
let adj = f32::from(zoom_adj);
|
||||
|
|
@ -120,7 +275,7 @@ impl Config {
|
|||
(self.opacity as f32) / 100.0
|
||||
}
|
||||
|
||||
// Get a sorted and adjusted for duplicates list of profiles names and ids
|
||||
// Get a sorted and adjusted for duplicates list of profile names and ids
|
||||
pub fn profile_names(&self) -> Vec<(String, ProfileId)> {
|
||||
let mut profile_names = Vec::<(String, ProfileId)>::with_capacity(self.profiles.len());
|
||||
for (profile_id, profile) in self.profiles.iter() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue