增加QScheme主题样式的生成与导出。
This commit is contained in:
		| @@ -1,7 +1,8 @@ | ||||
| use palette::{ | ||||
|     cam16::{Cam16Jch, Parameters}, | ||||
|     convert::FromColorUnclamped, | ||||
|     Hsl, IsWithinBounds, Srgb, | ||||
|     luma::Luma, | ||||
|     Hsl, IntoColor, IsWithinBounds, Oklab, Oklch, Srgb, | ||||
| }; | ||||
|  | ||||
| pub fn map_cam16jch_to_srgb(origin: &Cam16Jch<f32>) -> Srgb { | ||||
| @@ -44,3 +45,43 @@ pub fn map_hsl_to_srgb(origin: &Hsl) -> Srgb { | ||||
| pub fn map_hsl_to_srgb_hex(origin: &Hsl) -> String { | ||||
|     format!("{:x}", map_hsl_to_srgb(origin).into_format::<u8>()) | ||||
| } | ||||
|  | ||||
| pub fn map_oklch_to_luma(origin: &Oklch) -> Luma { | ||||
|     let lab_color: Oklab = (*origin).into_color(); | ||||
|     let linear_rgb = Srgb::from_linear(lab_color.into_color()); | ||||
|     let luma_color: Luma = linear_rgb.into_color(); | ||||
|     luma_color | ||||
| } | ||||
|  | ||||
| pub fn map_oklch_to_srgb(origin: &Oklch) -> Srgb { | ||||
|     Srgb::from_linear::<f32>((*origin).into_color()) | ||||
| } | ||||
|  | ||||
| pub fn map_oklch_to_srgb_hex(origin: &Oklch) -> String { | ||||
|     format!("{:x}", map_oklch_to_srgb(origin).into_format::<u8>()) | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! parse_to_oklch { | ||||
|     ($origin: ident) => { | ||||
|         palette::Oklch::from_color( | ||||
|             palette::Srgb::from_str($origin) | ||||
|                 .map_err(|_| crate::errors::ColorError::UnrecogniazedRGB($origin.to_string()))? | ||||
|                 .into_format::<f32>(), | ||||
|         ) | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! parse_option_to_oklch { | ||||
|     ($origin: ident) => { | ||||
|         $origin | ||||
|             .map(|color| { | ||||
|                 let rgb = palette::Srgb::from_str(color) | ||||
|                     .map_err(|_| crate::errors::ColorError::UnrecogniazedRGB(color.to_string()))? | ||||
|                     .into_format::<f32>(); | ||||
|                 Ok(palette::Oklch::from_color(rgb)) | ||||
|             }) | ||||
|             .transpose()? | ||||
|     }; | ||||
| } | ||||
|   | ||||
| @@ -2,12 +2,14 @@ use std::collections::HashMap; | ||||
|  | ||||
| use material_design_2::MaterialDesign2Scheme; | ||||
| use material_design_3::MaterialDesign3Scheme; | ||||
| use q_style::{QScheme, SchemeSetting}; | ||||
| use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; | ||||
|  | ||||
| use crate::errors; | ||||
|  | ||||
| pub mod material_design_2; | ||||
| pub mod material_design_3; | ||||
| pub mod q_style; | ||||
|  | ||||
| pub trait SchemeExport { | ||||
|     fn output_css_variables(&self) -> String; | ||||
| @@ -57,3 +59,73 @@ pub fn generate_material_design_2_scheme( | ||||
|     )) | ||||
|     .map_err(|_| errors::ColorError::UnableToAssembleOutput)?) | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| pub fn generate_q_scheme_automatically( | ||||
|     primary_color: &str, | ||||
|     danger_color: &str, | ||||
|     success_color: &str, | ||||
|     warning_color: &str, | ||||
|     info_color: &str, | ||||
|     fg_color: &str, | ||||
|     bg_color: &str, | ||||
|     setting: JsValue, | ||||
| ) -> Result<JsValue, errors::ColorError> { | ||||
|     let settings: SchemeSetting = serde_wasm_bindgen::from_value(setting) | ||||
|         .map_err(|_| errors::ColorError::UnableToParseArgument)?; | ||||
|     let scheme = QScheme::new( | ||||
|         primary_color, | ||||
|         danger_color, | ||||
|         success_color, | ||||
|         warning_color, | ||||
|         info_color, | ||||
|         fg_color, | ||||
|         bg_color, | ||||
|         settings, | ||||
|     )?; | ||||
|     Ok(serde_wasm_bindgen::to_value(&( | ||||
|         scheme.clone(), | ||||
|         scheme.output_css_variables(), | ||||
|         scheme.output_scss_variables(), | ||||
|         scheme.output_javascript_object(), | ||||
|     )) | ||||
|     .map_err(|_| errors::ColorError::UnableToAssembleOutput)?) | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| pub fn generate_q_scheme_manually( | ||||
|     primary_color: &str, | ||||
|     secondary_color: Option<String>, | ||||
|     tertiary_color: Option<String>, | ||||
|     accent_color: Option<String>, | ||||
|     danger_color: &str, | ||||
|     success_color: &str, | ||||
|     warning_color: &str, | ||||
|     info_color: &str, | ||||
|     fg_color: &str, | ||||
|     bg_color: &str, | ||||
|     setting: JsValue, | ||||
| ) -> Result<JsValue, errors::ColorError> { | ||||
|     let settings: SchemeSetting = serde_wasm_bindgen::from_value(setting) | ||||
|         .map_err(|_| errors::ColorError::UnableToParseArgument)?; | ||||
|     let scheme = QScheme::custom( | ||||
|         primary_color, | ||||
|         secondary_color.as_deref(), | ||||
|         tertiary_color.as_deref(), | ||||
|         accent_color.as_deref(), | ||||
|         danger_color, | ||||
|         success_color, | ||||
|         warning_color, | ||||
|         info_color, | ||||
|         fg_color, | ||||
|         bg_color, | ||||
|         settings, | ||||
|     )?; | ||||
|     Ok(serde_wasm_bindgen::to_value(&( | ||||
|         scheme.clone(), | ||||
|         scheme.output_css_variables(), | ||||
|         scheme.output_scss_variables(), | ||||
|         scheme.output_javascript_object(), | ||||
|     )) | ||||
|     .map_err(|_| errors::ColorError::UnableToAssembleOutput)?) | ||||
| } | ||||
|   | ||||
							
								
								
									
										241
									
								
								color-module/src/schemes/q_style/baseline.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								color-module/src/schemes/q_style/baseline.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | ||||
| use palette::{ | ||||
|     color_theory::{Analogous, Complementary, SplitComplementary, Tetradic, Triadic}, | ||||
|     Oklch, ShiftHue, | ||||
| }; | ||||
| use serde::{ser::SerializeStruct, Serialize}; | ||||
|  | ||||
| use crate::convert::map_oklch_to_srgb_hex; | ||||
|  | ||||
| use super::{ | ||||
|     color_set::ColorSet, | ||||
|     neutral_swatch::NeutralSwatch, | ||||
|     scheme_setting::{ColorExpand, SchemeSetting}, | ||||
| }; | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct Baseline { | ||||
|     pub primary: ColorSet, | ||||
|     pub secondary: Option<ColorSet>, | ||||
|     pub tertiary: Option<ColorSet>, | ||||
|     pub accent: Option<ColorSet>, | ||||
|     pub neutral: ColorSet, | ||||
|     pub danger: ColorSet, | ||||
|     pub success: ColorSet, | ||||
|     pub warning: ColorSet, | ||||
|     pub info: ColorSet, | ||||
|     pub outline: ColorSet, | ||||
|     pub foreground: Oklch, | ||||
|     pub background: Oklch, | ||||
|     _neutral_swatch: NeutralSwatch, | ||||
| } | ||||
|  | ||||
| impl Baseline { | ||||
|     pub fn custom( | ||||
|         primary: &Oklch, | ||||
|         secondary: &Option<Oklch>, | ||||
|         tertiary: &Option<Oklch>, | ||||
|         accent: &Option<Oklch>, | ||||
|         danger: &Oklch, | ||||
|         success: &Oklch, | ||||
|         warning: &Oklch, | ||||
|         info: &Oklch, | ||||
|         foreground: &Oklch, | ||||
|         background: &Oklch, | ||||
|         setting: SchemeSetting, | ||||
|     ) -> Self { | ||||
|         let neutral_swatch = NeutralSwatch::new(*foreground, *background); | ||||
|         let neutral_color = neutral_swatch.get(primary.l); | ||||
|         let outline_color = neutral_swatch.get(background.l * 0.7); | ||||
|  | ||||
|         Self { | ||||
|             primary: ColorSet::new(primary, &neutral_swatch, &setting), | ||||
|             secondary: secondary.map(|color| ColorSet::new(&color, &neutral_swatch, &setting)), | ||||
|             tertiary: tertiary.map(|color| ColorSet::new(&color, &neutral_swatch, &setting)), | ||||
|             accent: accent.map(|color| ColorSet::new(&color, &neutral_swatch, &setting)), | ||||
|             neutral: ColorSet::new(&neutral_color, &neutral_swatch, &setting), | ||||
|             danger: ColorSet::new(danger, &neutral_swatch, &setting), | ||||
|             success: ColorSet::new(success, &neutral_swatch, &setting), | ||||
|             warning: ColorSet::new(warning, &neutral_swatch, &setting), | ||||
|             info: ColorSet::new(info, &neutral_swatch, &setting), | ||||
|             outline: ColorSet::new(&outline_color, &neutral_swatch, &setting), | ||||
|             foreground: *foreground, | ||||
|             background: *background, | ||||
|             _neutral_swatch: neutral_swatch, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn new( | ||||
|         primary: &Oklch, | ||||
|         danger: &Oklch, | ||||
|         success: &Oklch, | ||||
|         warning: &Oklch, | ||||
|         info: &Oklch, | ||||
|         foreground: &Oklch, | ||||
|         background: &Oklch, | ||||
|         setting: SchemeSetting, | ||||
|     ) -> Self { | ||||
|         let (secondary, tertiary, accent) = match setting.expand_method { | ||||
|             ColorExpand::Complementary => (Some(primary.complementary()), None, None), | ||||
|             ColorExpand::Analogous => { | ||||
|                 let analogous_color = primary.analogous(); | ||||
|                 (Some(analogous_color.0), Some(analogous_color.1), None) | ||||
|             } | ||||
|             ColorExpand::AnalogousAndComplementary => { | ||||
|                 let analogous_color = primary.analogous(); | ||||
|                 ( | ||||
|                     Some(analogous_color.0), | ||||
|                     Some(analogous_color.1), | ||||
|                     Some(primary.complementary()), | ||||
|                 ) | ||||
|             } | ||||
|             ColorExpand::Triadic => { | ||||
|                 let triadic_color = primary.triadic(); | ||||
|                 (Some(triadic_color.0), Some(triadic_color.1), None) | ||||
|             } | ||||
|             ColorExpand::SplitComplementary => { | ||||
|                 let split_complementary_color = primary.split_complementary(); | ||||
|                 ( | ||||
|                     Some(split_complementary_color.0), | ||||
|                     None, | ||||
|                     Some(split_complementary_color.1), | ||||
|                 ) | ||||
|             } | ||||
|             ColorExpand::Tetradic => { | ||||
|                 let tetradic_color = primary.tetradic(); | ||||
|                 ( | ||||
|                     Some(tetradic_color.0), | ||||
|                     Some(tetradic_color.2), | ||||
|                     Some(tetradic_color.1), | ||||
|                 ) | ||||
|             } | ||||
|             ColorExpand::Square => { | ||||
|                 let c_90 = primary.shift_hue(90.0); | ||||
|                 let complementary = primary.complementary(); | ||||
|                 let c_270 = primary.shift_hue(270.0); | ||||
|                 (Some(c_90), Some(c_270), Some(complementary)) | ||||
|             } | ||||
|         }; | ||||
|  | ||||
|         Self::custom( | ||||
|             primary, &secondary, &tertiary, &accent, danger, success, warning, info, foreground, | ||||
|             background, setting, | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     pub fn to_css_variables(&self, prefix: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         variables.extend(self.primary.to_css_variables(prefix, "primary")); | ||||
|         if let Some(secondary) = &self.secondary { | ||||
|             variables.extend(secondary.to_css_variables(prefix, "secondary")); | ||||
|         } | ||||
|         if let Some(tertiary) = &self.tertiary { | ||||
|             variables.extend(tertiary.to_css_variables(prefix, "tertiary")); | ||||
|         } | ||||
|         if let Some(accent) = &self.accent { | ||||
|             variables.extend(accent.to_css_variables(prefix, "accent")); | ||||
|         } | ||||
|         variables.extend(self.neutral.to_css_variables(prefix, "neutral")); | ||||
|         variables.extend(self.danger.to_css_variables(prefix, "danger")); | ||||
|         variables.extend(self.success.to_css_variables(prefix, "success")); | ||||
|         variables.extend(self.warning.to_css_variables(prefix, "warning")); | ||||
|         variables.extend(self.info.to_css_variables(prefix, "info")); | ||||
|         variables.extend(self.outline.to_css_variables(prefix, "outline")); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-foreground: #{};", | ||||
|             prefix, | ||||
|             map_oklch_to_srgb_hex(&self.foreground) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-background: #{};", | ||||
|             prefix, | ||||
|             map_oklch_to_srgb_hex(&self.background) | ||||
|         )); | ||||
|  | ||||
|         variables | ||||
|     } | ||||
|  | ||||
|     pub fn to_scss_variables(&self, prefix: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         variables.extend(self.primary.to_scss_variables(prefix, "primary")); | ||||
|         if let Some(secondary) = &self.secondary { | ||||
|             variables.extend(secondary.to_scss_variables(prefix, "secondary")); | ||||
|         } | ||||
|         if let Some(tertiary) = &self.tertiary { | ||||
|             variables.extend(tertiary.to_scss_variables(prefix, "tertiary")); | ||||
|         } | ||||
|         if let Some(accent) = &self.accent { | ||||
|             variables.extend(accent.to_scss_variables(prefix, "accent")); | ||||
|         } | ||||
|         variables.extend(self.neutral.to_scss_variables(prefix, "neutral")); | ||||
|         variables.extend(self.danger.to_scss_variables(prefix, "danger")); | ||||
|         variables.extend(self.success.to_scss_variables(prefix, "success")); | ||||
|         variables.extend(self.warning.to_scss_variables(prefix, "warning")); | ||||
|         variables.extend(self.info.to_scss_variables(prefix, "info")); | ||||
|         variables.extend(self.outline.to_scss_variables(prefix, "outline")); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-foreground: #{};", | ||||
|             prefix, | ||||
|             map_oklch_to_srgb_hex(&self.foreground) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-background: #{};", | ||||
|             prefix, | ||||
|             map_oklch_to_srgb_hex(&self.background) | ||||
|         )); | ||||
|  | ||||
|         variables | ||||
|     } | ||||
|  | ||||
|     pub fn to_javascript_fields(&self) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         variables.extend(self.primary.to_javascript_fields("primary")); | ||||
|         if let Some(secondary) = &self.secondary { | ||||
|             variables.extend(secondary.to_javascript_fields("secondary")); | ||||
|         } | ||||
|         if let Some(tertiary) = &self.tertiary { | ||||
|             variables.extend(tertiary.to_javascript_fields("tertiary")); | ||||
|         } | ||||
|         if let Some(accent) = &self.accent { | ||||
|             variables.extend(accent.to_javascript_fields("accent")); | ||||
|         } | ||||
|         variables.extend(self.neutral.to_javascript_fields("neutral")); | ||||
|         variables.extend(self.danger.to_javascript_fields("danger")); | ||||
|         variables.extend(self.success.to_javascript_fields("success")); | ||||
|         variables.extend(self.warning.to_javascript_fields("warning")); | ||||
|         variables.extend(self.info.to_javascript_fields("info")); | ||||
|         variables.extend(self.outline.to_javascript_fields("outline")); | ||||
|         variables.push(format!( | ||||
|             "foreground: '#{}',", | ||||
|             map_oklch_to_srgb_hex(&self.foreground) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "background: '#{}',", | ||||
|             map_oklch_to_srgb_hex(&self.background) | ||||
|         )); | ||||
|  | ||||
|         variables | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Serialize for Baseline { | ||||
|     fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> { | ||||
|         let mut map = serializer.serialize_struct("Baseline", 13)?; | ||||
|  | ||||
|         map.serialize_field("primary", &self.primary)?; | ||||
|         map.serialize_field("secondary", &self.secondary)?; | ||||
|         map.serialize_field("tertiary", &self.tertiary)?; | ||||
|         map.serialize_field("accent", &self.accent)?; | ||||
|         map.serialize_field("neutral", &self.neutral)?; | ||||
|         map.serialize_field("danger", &self.danger)?; | ||||
|         map.serialize_field("success", &self.success)?; | ||||
|         map.serialize_field("warning", &self.warning)?; | ||||
|         map.serialize_field("info", &self.info)?; | ||||
|         map.serialize_field("outline", &self.outline)?; | ||||
|         map.serialize_field("foreground", &map_oklch_to_srgb_hex(&self.foreground))?; | ||||
|         map.serialize_field("background", &map_oklch_to_srgb_hex(&self.background))?; | ||||
|         map.end() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										327
									
								
								color-module/src/schemes/q_style/color_set.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										327
									
								
								color-module/src/schemes/q_style/color_set.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,327 @@ | ||||
| use palette::{color_difference::Wcag21RelativeContrast, luma::Luma, Oklch}; | ||||
| use serde::{ser::SerializeStruct, Serialize}; | ||||
|  | ||||
| use crate::convert::{map_oklch_to_luma, map_oklch_to_srgb_hex}; | ||||
|  | ||||
| use super::{ | ||||
|     neutral_swatch::NeutralSwatch, | ||||
|     scheme_setting::{SchemeSetting, WACGSetting}, | ||||
| }; | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct ColorSet { | ||||
|     pub root: Oklch, | ||||
|     pub hover: Oklch, | ||||
|     pub active: Oklch, | ||||
|     pub focus: Oklch, | ||||
|     pub disabled: Oklch, | ||||
|     pub on_root: Oklch, | ||||
|     pub on_hover: Oklch, | ||||
|     pub on_active: Oklch, | ||||
|     pub on_focus: Oklch, | ||||
|     pub on_disabled: Oklch, | ||||
| } | ||||
|  | ||||
| fn fit_to_wacg(reference: &Oklch, neutral_swatch: &NeutralSwatch, ratio: f32) -> Oklch { | ||||
|     let reference_luma = map_oklch_to_luma(reference); | ||||
|     let mut new_target = neutral_swatch.get(reference.l); | ||||
|     let quick_factor: f32 = if reference.l <= 0.5 { 0.05 } else { -0.05 }; | ||||
|     let fine_factor: f32 = if reference.l <= 0.5 { 0.01 } else { -0.01 }; | ||||
|  | ||||
|     let match_wacg = |original: &Oklch<f32>, reference: &Luma| { | ||||
|         let luma = map_oklch_to_luma(original); | ||||
|         luma.relative_contrast(*reference) | ||||
|     }; | ||||
|  | ||||
|     while match_wacg(&new_target, &reference_luma) < ratio { | ||||
|         new_target.l = new_target.l * (1.0 + quick_factor); | ||||
|         if new_target.l > 1.0 { | ||||
|             new_target.l = 1.0; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     while match_wacg(&new_target, &reference_luma) < ratio { | ||||
|         new_target.l = new_target.l * (1.0 + fine_factor); | ||||
|         if new_target.l > 1.0 { | ||||
|             new_target.l = 1.0; | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     new_target | ||||
| } | ||||
|  | ||||
| impl ColorSet { | ||||
|     pub fn new(color: &Oklch, neutral_swatch: &NeutralSwatch, setting: &SchemeSetting) -> Self { | ||||
|         let root = color.clone(); | ||||
|         let hover = color * setting.hover; | ||||
|         let active = color * setting.active; | ||||
|         let focus = color * setting.focus; | ||||
|         let disabled = color * setting.disabled; | ||||
|  | ||||
|         let (on_root, on_hover, on_active, on_focus, on_disabled) = match setting.wacg_follows { | ||||
|             WACGSetting::Fixed => ( | ||||
|                 neutral_swatch.get(root.l), | ||||
|                 neutral_swatch.get(hover.l), | ||||
|                 neutral_swatch.get(active.l), | ||||
|                 neutral_swatch.get(focus.l), | ||||
|                 neutral_swatch.get(disabled.l), | ||||
|             ), | ||||
|             WACGSetting::AutomaticAA => ( | ||||
|                 fit_to_wacg(&root, neutral_swatch, 4.5), | ||||
|                 fit_to_wacg(&hover, neutral_swatch, 4.5), | ||||
|                 fit_to_wacg(&active, neutral_swatch, 4.5), | ||||
|                 fit_to_wacg(&focus, neutral_swatch, 4.5), | ||||
|                 fit_to_wacg(&disabled, neutral_swatch, 4.5), | ||||
|             ), | ||||
|             WACGSetting::AutomaticAAA => ( | ||||
|                 fit_to_wacg(&root, neutral_swatch, 7.0), | ||||
|                 fit_to_wacg(&hover, neutral_swatch, 7.0), | ||||
|                 fit_to_wacg(&active, neutral_swatch, 7.0), | ||||
|                 fit_to_wacg(&focus, neutral_swatch, 7.0), | ||||
|                 fit_to_wacg(&disabled, neutral_swatch, 7.0), | ||||
|             ), | ||||
|         }; | ||||
|  | ||||
|         Self { | ||||
|             root, | ||||
|             hover, | ||||
|             active, | ||||
|             focus, | ||||
|             disabled, | ||||
|             on_root, | ||||
|             on_hover, | ||||
|             on_active, | ||||
|             on_focus, | ||||
|             on_disabled, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn to_css_variables(&self, prefix: &str, name: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         variables.push(format!( | ||||
|             "--color-{}-{}: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.root) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-{}-hover: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.hover) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-{}-active: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.active) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-{}-focus: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.focus) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-{}-disabled: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.disabled) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-on-{}: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_root) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-on-{}-hover: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_hover) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-on-{}-active: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_active) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-on-{}-focus: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_focus) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "--color-{}-on-{}-disabled: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_disabled) | ||||
|         )); | ||||
|  | ||||
|         variables | ||||
|     } | ||||
|  | ||||
|     pub fn to_scss_variables(&self, prefix: &str, name: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         variables.push(format!( | ||||
|             "$color-{}-{}: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.root) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-{}-hover: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.hover) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-{}-active: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.active) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-{}-focus: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.focus) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-{}-disabled: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.disabled) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-on-{}: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_root) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-on-{}-hover: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_hover) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-on-{}-active: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_active) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-on-{}-focus: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_focus) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "$color-{}-on-{}-disabled: #{};", | ||||
|             prefix, | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.on_disabled) | ||||
|         )); | ||||
|  | ||||
|         variables | ||||
|     } | ||||
|  | ||||
|     pub fn to_javascript_fields(&self, name: &str) -> Vec<String> { | ||||
|         let mut variables = Vec::new(); | ||||
|         let capitalized_name = name | ||||
|             .chars() | ||||
|             .next() | ||||
|             .unwrap() | ||||
|             .to_ascii_uppercase() | ||||
|             .to_string() | ||||
|             + &name[1..]; | ||||
|  | ||||
|         variables.push(format!( | ||||
|             "{}: '#{}',", | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.root) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "{}Hover: '#{}',", | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.hover) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "{}Active: '#{}',", | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.active) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "{}Focus: '#{}',", | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.focus) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "{}Disabled: '#{}',", | ||||
|             name, | ||||
|             map_oklch_to_srgb_hex(&self.disabled) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "on{}: '#{}',", | ||||
|             capitalized_name, | ||||
|             map_oklch_to_srgb_hex(&self.on_root) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "on{}Hover: '#{}',", | ||||
|             capitalized_name, | ||||
|             map_oklch_to_srgb_hex(&self.on_hover) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "on{}Active: '#{}',", | ||||
|             capitalized_name, | ||||
|             map_oklch_to_srgb_hex(&self.on_active) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "on{}Focus: '#{}',", | ||||
|             capitalized_name, | ||||
|             map_oklch_to_srgb_hex(&self.on_focus) | ||||
|         )); | ||||
|         variables.push(format!( | ||||
|             "on{}Disabled: '#{}',", | ||||
|             capitalized_name, | ||||
|             map_oklch_to_srgb_hex(&self.on_disabled) | ||||
|         )); | ||||
|  | ||||
|         variables | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Serialize for ColorSet { | ||||
|     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> | ||||
|     where | ||||
|         S: serde::Serializer, | ||||
|     { | ||||
|         let root = map_oklch_to_srgb_hex(&self.root); | ||||
|         let hover = map_oklch_to_srgb_hex(&self.hover); | ||||
|         let active = map_oklch_to_srgb_hex(&self.active); | ||||
|         let focus = map_oklch_to_srgb_hex(&self.focus); | ||||
|         let disabled = map_oklch_to_srgb_hex(&self.disabled); | ||||
|         let on_root = map_oklch_to_srgb_hex(&self.on_root); | ||||
|         let on_hover = map_oklch_to_srgb_hex(&self.on_hover); | ||||
|         let on_active = map_oklch_to_srgb_hex(&self.on_active); | ||||
|         let on_focus = map_oklch_to_srgb_hex(&self.on_focus); | ||||
|         let on_disabled = map_oklch_to_srgb_hex(&self.on_disabled); | ||||
|  | ||||
|         let mut state = serializer.serialize_struct("ColorSet", 10)?; | ||||
|         state.serialize_field("root", &root)?; | ||||
|         state.serialize_field("hover", &hover)?; | ||||
|         state.serialize_field("active", &active)?; | ||||
|         state.serialize_field("focus", &focus)?; | ||||
|         state.serialize_field("disabled", &disabled)?; | ||||
|         state.serialize_field("onRoot", &on_root)?; | ||||
|         state.serialize_field("onHover", &on_hover)?; | ||||
|         state.serialize_field("onActive", &on_active)?; | ||||
|         state.serialize_field("onFocus", &on_focus)?; | ||||
|         state.serialize_field("onDisabled", &on_disabled)?; | ||||
|         state.end() | ||||
|     } | ||||
| } | ||||
							
								
								
									
										196
									
								
								color-module/src/schemes/q_style/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										196
									
								
								color-module/src/schemes/q_style/mod.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,196 @@ | ||||
| use std::str::FromStr; | ||||
|  | ||||
| use baseline::Baseline; | ||||
| use palette::FromColor; | ||||
| use scheme_setting::{ColorExpand, WACGSetting}; | ||||
| use serde::Serialize; | ||||
| use strum::IntoEnumIterator; | ||||
| use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; | ||||
|  | ||||
| use crate::{errors, parse_option_to_oklch, parse_to_oklch}; | ||||
|  | ||||
| use super::SchemeExport; | ||||
|  | ||||
| mod baseline; | ||||
| mod color_set; | ||||
| mod neutral_swatch; | ||||
| mod scheme_setting; | ||||
|  | ||||
| pub use scheme_setting::SchemeSetting; | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| pub struct QScheme { | ||||
|     pub light: Baseline, | ||||
|     pub dark: Baseline, | ||||
| } | ||||
|  | ||||
| impl QScheme { | ||||
|     pub fn new( | ||||
|         primary: &str, | ||||
|         danger: &str, | ||||
|         success: &str, | ||||
|         warning: &str, | ||||
|         info: &str, | ||||
|         foreground: &str, | ||||
|         background: &str, | ||||
|         setting: SchemeSetting, | ||||
|     ) -> Result<Self, errors::ColorError> { | ||||
|         let primary = parse_to_oklch!(primary); | ||||
|         let danger = parse_to_oklch!(danger); | ||||
|         let success = parse_to_oklch!(success); | ||||
|         let warning = parse_to_oklch!(warning); | ||||
|         let info = parse_to_oklch!(info); | ||||
|         let foreground = parse_to_oklch!(foreground); | ||||
|         let background = parse_to_oklch!(background); | ||||
|         Ok(Self { | ||||
|             light: Baseline::new( | ||||
|                 &primary, | ||||
|                 &danger, | ||||
|                 &success, | ||||
|                 &warning, | ||||
|                 &info, | ||||
|                 &foreground, | ||||
|                 &background, | ||||
|                 setting.clone(), | ||||
|             ), | ||||
|             dark: Baseline::new( | ||||
|                 &(primary * setting.dark_convert), | ||||
|                 &(danger * setting.dark_convert), | ||||
|                 &(success * setting.dark_convert), | ||||
|                 &(warning * setting.dark_convert), | ||||
|                 &(info * setting.dark_convert), | ||||
|                 &(foreground * setting.dark_convert), | ||||
|                 &(background * setting.dark_convert), | ||||
|                 setting.clone(), | ||||
|             ), | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub fn custom( | ||||
|         primary: &str, | ||||
|         secondary: Option<&str>, | ||||
|         tertiary: Option<&str>, | ||||
|         accent: Option<&str>, | ||||
|         danger: &str, | ||||
|         success: &str, | ||||
|         warning: &str, | ||||
|         info: &str, | ||||
|         foreground: &str, | ||||
|         background: &str, | ||||
|         setting: SchemeSetting, | ||||
|     ) -> Result<Self, errors::ColorError> { | ||||
|         let primary = parse_to_oklch!(primary); | ||||
|         let secondary = parse_option_to_oklch!(secondary); | ||||
|         let tertiary = parse_option_to_oklch!(tertiary); | ||||
|         let accent = parse_option_to_oklch!(accent); | ||||
|         let danger = parse_to_oklch!(danger); | ||||
|         let success = parse_to_oklch!(success); | ||||
|         let warning = parse_to_oklch!(warning); | ||||
|         let info = parse_to_oklch!(info); | ||||
|         let foreground = parse_to_oklch!(foreground); | ||||
|         let background = parse_to_oklch!(background); | ||||
|  | ||||
|         Ok(Self { | ||||
|             light: Baseline::custom( | ||||
|                 &primary, | ||||
|                 &secondary, | ||||
|                 &tertiary, | ||||
|                 &accent, | ||||
|                 &danger, | ||||
|                 &success, | ||||
|                 &warning, | ||||
|                 &info, | ||||
|                 &foreground, | ||||
|                 &background, | ||||
|                 setting.clone(), | ||||
|             ), | ||||
|             dark: Baseline::custom( | ||||
|                 &(primary * setting.dark_convert), | ||||
|                 &secondary.map(|color| color * setting.dark_convert), | ||||
|                 &tertiary.map(|color| color * setting.dark_convert), | ||||
|                 &accent.map(|color| color * setting.dark_convert), | ||||
|                 &(danger * setting.dark_convert), | ||||
|                 &(success * setting.dark_convert), | ||||
|                 &(warning * setting.dark_convert), | ||||
|                 &(info * setting.dark_convert), | ||||
|                 &(foreground * setting.dark_convert), | ||||
|                 &(background * setting.dark_convert), | ||||
|                 setting.clone(), | ||||
|             ), | ||||
|         }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl SchemeExport for QScheme { | ||||
|     fn output_css_variables(&self) -> String { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         variables.extend(self.light.to_css_variables("light")); | ||||
|         variables.extend(self.dark.to_css_variables("dark")); | ||||
|  | ||||
|         variables.join("\n") | ||||
|     } | ||||
|  | ||||
|     fn output_scss_variables(&self) -> String { | ||||
|         let mut variables = Vec::new(); | ||||
|  | ||||
|         variables.extend(self.light.to_scss_variables("light")); | ||||
|         variables.extend(self.dark.to_scss_variables("dark")); | ||||
|  | ||||
|         variables.join("\n") | ||||
|     } | ||||
|  | ||||
|     fn output_javascript_object(&self) -> String { | ||||
|         let mut javascript_object = Vec::new(); | ||||
|  | ||||
|         javascript_object.push("{".to_string()); | ||||
|         javascript_object.push("  light: {".to_string()); | ||||
|         javascript_object.extend( | ||||
|             self.light | ||||
|                 .to_javascript_fields() | ||||
|                 .into_iter() | ||||
|                 .map(|c| format!("    {}", c)) | ||||
|                 .collect::<Vec<_>>(), | ||||
|         ); | ||||
|         javascript_object.push("  },".to_string()); | ||||
|         javascript_object.push("  dark: {".to_string()); | ||||
|         javascript_object.extend( | ||||
|             self.dark | ||||
|                 .to_javascript_fields() | ||||
|                 .into_iter() | ||||
|                 .map(|c| format!("    {}", c)) | ||||
|                 .collect::<Vec<_>>(), | ||||
|         ); | ||||
|         javascript_object.push("}".to_string()); | ||||
|  | ||||
|         javascript_object.join("\n") | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| pub fn q_scheme_color_expanding_methods() -> Result<JsValue, String> { | ||||
|     let methods = ColorExpand::iter() | ||||
|         .map(|variant| { | ||||
|             serde_json::json!({ | ||||
|                 "label": variant.label(), | ||||
|                 "value": variant.to_string(), | ||||
|             }) | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
|  | ||||
|     serde_wasm_bindgen::to_value(&methods).map_err(|e| e.to_string()) | ||||
| } | ||||
|  | ||||
| #[wasm_bindgen] | ||||
| pub fn q_scheme_wacg_settings() -> Result<JsValue, String> { | ||||
|     let settings = WACGSetting::iter() | ||||
|         .map(|setting| { | ||||
|             serde_json::json!({ | ||||
|                 "label": setting.label(), | ||||
|                 "value": setting.to_string(), | ||||
|             }) | ||||
|         }) | ||||
|         .collect::<Vec<_>>(); | ||||
|  | ||||
|     serde_wasm_bindgen::to_value(&settings).map_err(|e| e.to_string()) | ||||
| } | ||||
							
								
								
									
										58
									
								
								color-module/src/schemes/q_style/neutral_swatch.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										58
									
								
								color-module/src/schemes/q_style/neutral_swatch.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,58 @@ | ||||
| use palette::Oklch; | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct NeutralSwatch(Oklch, Oklch); | ||||
|  | ||||
| impl NeutralSwatch { | ||||
|     pub fn new(color1: Oklch, color2: Oklch) -> Self { | ||||
|         if color1.l < color2.l { | ||||
|             NeutralSwatch( | ||||
|                 Oklch { | ||||
|                     l: color1.l * 0.4, | ||||
|                     chroma: color1.chroma * 0.1, | ||||
|                     ..color1 | ||||
|                 }, | ||||
|                 Oklch { | ||||
|                     l: color2.l * 1.6, | ||||
|                     chroma: color2.chroma * 0.1, | ||||
|                     ..color2 | ||||
|                 }, | ||||
|             ) | ||||
|         } else { | ||||
|             NeutralSwatch( | ||||
|                 Oklch { | ||||
|                     l: color2.l * 0.4, | ||||
|                     chroma: color2.chroma * 0.1, | ||||
|                     ..color2 | ||||
|                 }, | ||||
|                 Oklch { | ||||
|                     l: color1.l * 1.6, | ||||
|                     chroma: color1.chroma * 0.1, | ||||
|                     ..color1 | ||||
|                 }, | ||||
|             ) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn get(&self, percent: f32) -> Oklch { | ||||
|         let start_hue = self.0.hue.into_positive_degrees(); | ||||
|         let end_hue = self.1.hue.into_positive_degrees(); | ||||
|  | ||||
|         let hue = if (start_hue - end_hue).abs() > 180.0 { | ||||
|             if end_hue > start_hue { | ||||
|                 start_hue + (end_hue + 360.0 - start_hue) * percent | ||||
|             } else { | ||||
|                 start_hue + (end_hue - start_hue + 360.0) * percent | ||||
|             } | ||||
|         } else { | ||||
|             start_hue + (end_hue - start_hue) * percent | ||||
|         } | ||||
|         .rem_euclid(360.0); | ||||
|  | ||||
|         Oklch { | ||||
|             l: percent, | ||||
|             chroma: self.0.chroma + (self.1.chroma - self.0.chroma) * percent, | ||||
|             hue: hue.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										139
									
								
								color-module/src/schemes/q_style/scheme_setting.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										139
									
								
								color-module/src/schemes/q_style/scheme_setting.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,139 @@ | ||||
| use std::ops::Mul; | ||||
|  | ||||
| use palette::Oklch; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use strum::{Display, EnumIter, EnumString}; | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, Serialize, Deserialize)] | ||||
| pub struct ColorShifting { | ||||
|     pub chroma: f32, | ||||
|     pub lightness: f32, | ||||
| } | ||||
|  | ||||
| impl Mul<ColorShifting> for Oklch<f32> { | ||||
|     type Output = Oklch<f32>; | ||||
|  | ||||
|     fn mul(self, rhs: ColorShifting) -> Self::Output { | ||||
|         Oklch::new( | ||||
|             self.l | ||||
|                 + if rhs.lightness > 0.0 { | ||||
|                     (1.0 - self.l) * rhs.lightness | ||||
|                 } else { | ||||
|                     self.l * rhs.lightness * -1.0 | ||||
|                 }, | ||||
|             self.chroma | ||||
|                 + if rhs.chroma > 0.0 { | ||||
|                     (100.0 - self.chroma) * rhs.chroma | ||||
|                 } else { | ||||
|                     -(self.chroma * rhs.chroma) | ||||
|                 }, | ||||
|             self.hue, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Mul<ColorShifting> for &Oklch<f32> { | ||||
|     type Output = Oklch<f32>; | ||||
|  | ||||
|     fn mul(self, rhs: ColorShifting) -> Self::Output { | ||||
|         Oklch::new( | ||||
|             self.l | ||||
|                 + if rhs.lightness > 0.0 { | ||||
|                     (1.0 - self.l) * rhs.lightness | ||||
|                 } else { | ||||
|                     self.l * rhs.lightness * -1.0 | ||||
|                 }, | ||||
|             self.chroma | ||||
|                 + if rhs.chroma > 0.0 { | ||||
|                     (100.0 - self.chroma) * rhs.chroma | ||||
|                 } else { | ||||
|                     -(self.chroma * rhs.chroma) | ||||
|                 }, | ||||
|             self.hue, | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, Serialize, Deserialize)] | ||||
| pub struct SchemeSetting { | ||||
|     pub hover: ColorShifting, | ||||
|     pub active: ColorShifting, | ||||
|     pub focus: ColorShifting, | ||||
|     pub disabled: ColorShifting, | ||||
|     pub dark_convert: ColorShifting, | ||||
|     pub expand_method: ColorExpand, | ||||
|     pub wacg_follows: WACGSetting, | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, Display, EnumString, EnumIter, Serialize, Deserialize)] | ||||
| #[strum(serialize_all = "lowercase")] | ||||
| pub enum ColorExpand { | ||||
|     Complementary, | ||||
|     Analogous, | ||||
|     AnalogousAndComplementary, | ||||
|     Triadic, | ||||
|     SplitComplementary, | ||||
|     Tetradic, | ||||
|     Square, | ||||
| } | ||||
|  | ||||
| impl ColorExpand { | ||||
|     pub fn label(&self) -> String { | ||||
|         match self { | ||||
|             ColorExpand::Complementary => "Complementary".to_string(), | ||||
|             ColorExpand::Analogous => "Analogous".to_string(), | ||||
|             ColorExpand::AnalogousAndComplementary => "Analogous and Complementary".to_string(), | ||||
|             ColorExpand::Triadic => "Triadic".to_string(), | ||||
|             ColorExpand::SplitComplementary => "Split Complementary".to_string(), | ||||
|             ColorExpand::Tetradic => "Tetradic".to_string(), | ||||
|             ColorExpand::Square => "Square".to_string(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone, Copy, Display, EnumString, EnumIter, Serialize, Deserialize)] | ||||
| #[strum(serialize_all = "lowercase")] | ||||
| pub enum WACGSetting { | ||||
|     Fixed, | ||||
|     AutomaticAA, | ||||
|     AutomaticAAA, | ||||
| } | ||||
|  | ||||
| impl WACGSetting { | ||||
|     pub fn label(&self) -> String { | ||||
|         match self { | ||||
|             WACGSetting::Fixed => "Fixed".to_string(), | ||||
|             WACGSetting::AutomaticAA => "Automatic AA".to_string(), | ||||
|             WACGSetting::AutomaticAAA => "Automatic AAA".to_string(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Default for SchemeSetting { | ||||
|     fn default() -> Self { | ||||
|         SchemeSetting { | ||||
|             hover: ColorShifting { | ||||
|                 chroma: 0.0, | ||||
|                 lightness: 0.3, | ||||
|             }, | ||||
|             active: ColorShifting { | ||||
|                 chroma: 0.0, | ||||
|                 lightness: -0.2, | ||||
|             }, | ||||
|             focus: ColorShifting { | ||||
|                 chroma: 0.0, | ||||
|                 lightness: 0.5, | ||||
|             }, | ||||
|             disabled: ColorShifting { | ||||
|                 chroma: -0.9, | ||||
|                 lightness: -0.2, | ||||
|             }, | ||||
|             dark_convert: ColorShifting { | ||||
|                 chroma: -0.3, | ||||
|                 lightness: -0.3, | ||||
|             }, | ||||
|             expand_method: ColorExpand::AnalogousAndComplementary, | ||||
|             wacg_follows: WACGSetting::AutomaticAAA, | ||||
|         } | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user