mirror of
https://github.com/danbulant/cushy
synced 2026-07-07 20:20:46 +00:00
ColorScheme[Builder]
This commit is contained in:
parent
40343e163f
commit
b1ae9efae2
3 changed files with 214 additions and 64 deletions
|
|
@ -2,7 +2,9 @@ use std::str::FromStr;
|
||||||
|
|
||||||
use gooey::animation::ZeroToOne;
|
use gooey::animation::ZeroToOne;
|
||||||
use gooey::styles::components::{TextColor, WidgetBackground};
|
use gooey::styles::components::{TextColor, WidgetBackground};
|
||||||
use gooey::styles::{ColorSource, ColorTheme, FixedTheme, SurfaceTheme, Theme, ThemePair};
|
use gooey::styles::{
|
||||||
|
ColorScheme, ColorSource, ColorTheme, FixedTheme, SurfaceTheme, Theme, ThemePair,
|
||||||
|
};
|
||||||
use gooey::value::{Dynamic, MapEach};
|
use gooey::value::{Dynamic, MapEach};
|
||||||
use gooey::widget::MakeWidget;
|
use gooey::widget::MakeWidget;
|
||||||
use gooey::widgets::{Input, Label, ModeSwitch, Scroll, Slider, Stack, Themed};
|
use gooey::widgets::{Input, Label, ModeSwitch, Scroll, Slider, Stack, Themed};
|
||||||
|
|
@ -10,19 +12,15 @@ use gooey::window::ThemeMode;
|
||||||
use gooey::Run;
|
use gooey::Run;
|
||||||
use kludgine::Color;
|
use kludgine::Color;
|
||||||
|
|
||||||
const PRIMARY_HUE: f32 = 240.;
|
|
||||||
const SECONDARY_HUE: f32 = 0.;
|
|
||||||
const TERTIARY_HUE: f32 = 330.;
|
|
||||||
const ERROR_HUE: f32 = 30.;
|
|
||||||
|
|
||||||
fn main() -> gooey::Result {
|
fn main() -> gooey::Result {
|
||||||
let (primary, primary_editor) = color_editor(PRIMARY_HUE, 0.8, "Primary");
|
let scheme = ColorScheme::default();
|
||||||
let (secondary, secondary_editor) = color_editor(SECONDARY_HUE, 0.3, "Secondary");
|
let (primary, primary_editor) = color_editor(scheme.primary, "Primary");
|
||||||
let (tertiary, tertiary_editor) = color_editor(TERTIARY_HUE, 0.3, "Tertiary");
|
let (secondary, secondary_editor) = color_editor(scheme.secondary, "Secondary");
|
||||||
let (error, error_editor) = color_editor(ERROR_HUE, 0.8, "Error");
|
let (tertiary, tertiary_editor) = color_editor(scheme.tertiary, "Tertiary");
|
||||||
let (neutral, neutral_editor) = color_editor(PRIMARY_HUE, 0.001, "Neutral");
|
let (error, error_editor) = color_editor(scheme.error, "Error");
|
||||||
|
let (neutral, neutral_editor) = color_editor(scheme.neutral, "Neutral");
|
||||||
let (neutral_variant, neutral_variant_editor) =
|
let (neutral_variant, neutral_variant_editor) =
|
||||||
color_editor(PRIMARY_HUE, 0.001, "Neutral Variant");
|
color_editor(scheme.neutral_variant, "Neutral Variant");
|
||||||
let (theme_mode, theme_switcher) = dark_mode_slider();
|
let (theme_mode, theme_switcher) = dark_mode_slider();
|
||||||
|
|
||||||
let default_theme = (
|
let default_theme = (
|
||||||
|
|
@ -35,14 +33,14 @@ fn main() -> gooey::Result {
|
||||||
)
|
)
|
||||||
.map_each(
|
.map_each(
|
||||||
|(primary, secondary, tertiary, error, neutral, neutral_variant)| {
|
|(primary, secondary, tertiary, error, neutral, neutral_variant)| {
|
||||||
ThemePair::from_sources(
|
ThemePair::from(ColorScheme {
|
||||||
*primary,
|
primary: *primary,
|
||||||
*secondary,
|
secondary: *secondary,
|
||||||
*tertiary,
|
tertiary: *tertiary,
|
||||||
*error,
|
error: *error,
|
||||||
*neutral,
|
neutral: *neutral,
|
||||||
*neutral_variant,
|
neutral_variant: *neutral_variant,
|
||||||
)
|
})
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -104,12 +102,11 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
fn color_editor(
|
fn color_editor(
|
||||||
initial_hue: f32,
|
initial_color: ColorSource,
|
||||||
initial_saturation: impl Into<ZeroToOne>,
|
|
||||||
label: &str,
|
label: &str,
|
||||||
) -> (Dynamic<ColorSource>, impl MakeWidget) {
|
) -> (Dynamic<ColorSource>, impl MakeWidget) {
|
||||||
let (hue, hue_text) = create_paired_string(initial_hue);
|
let (hue, hue_text) = create_paired_string(initial_color.hue.into_degrees());
|
||||||
let (saturation, saturation_text) = create_paired_string(initial_saturation.into());
|
let (saturation, saturation_text) = create_paired_string(initial_color.saturation);
|
||||||
|
|
||||||
let color =
|
let color =
|
||||||
(&hue, &saturation).map_each(|(hue, saturation)| ColorSource::new(*hue, *saturation));
|
(&hue, &saturation).map_each(|(hue, saturation)| ColorSource::new(*hue, *saturation));
|
||||||
|
|
|
||||||
|
|
@ -853,6 +853,12 @@ impl ZeroToOne {
|
||||||
pub fn into_f32(self) -> f32 {
|
pub fn into_f32(self) -> f32 {
|
||||||
self.0
|
self.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the result of 1.0 - `self`.
|
||||||
|
#[must_use]
|
||||||
|
pub fn one_minus(self) -> Self {
|
||||||
|
Self(1. - self.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for ZeroToOne {
|
impl Display for ZeroToOne {
|
||||||
|
|
@ -952,7 +958,15 @@ impl Div for ZeroToOne {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
|
||||||
fn div(self, rhs: Self) -> Self::Output {
|
fn div(self, rhs: Self) -> Self::Output {
|
||||||
Self(self.0 / rhs.0)
|
self / rhs.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Div<f32> for ZeroToOne {
|
||||||
|
type Output = Self;
|
||||||
|
|
||||||
|
fn div(self, rhs: f32) -> Self::Output {
|
||||||
|
Self(self.0 / rhs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
217
src/styles.rs
217
src/styles.rs
|
|
@ -915,54 +915,42 @@ pub struct ThemePair {
|
||||||
impl ThemePair {
|
impl ThemePair {
|
||||||
/// Returns a new theme generated from the provided color sources.
|
/// Returns a new theme generated from the provided color sources.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_sources(
|
pub fn from_scheme(scheme: &ColorScheme) -> Self {
|
||||||
primary: ColorSource,
|
|
||||||
secondary: ColorSource,
|
|
||||||
tertiary: ColorSource,
|
|
||||||
error: ColorSource,
|
|
||||||
neutral: ColorSource,
|
|
||||||
neutral_variant: ColorSource,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
light: Theme::light_from_sources(
|
light: Theme::light_from_sources(
|
||||||
primary,
|
scheme.primary,
|
||||||
secondary,
|
scheme.secondary,
|
||||||
tertiary,
|
scheme.tertiary,
|
||||||
error,
|
scheme.error,
|
||||||
neutral,
|
scheme.neutral,
|
||||||
neutral_variant,
|
scheme.neutral_variant,
|
||||||
),
|
),
|
||||||
dark: Theme::dark_from_sources(
|
dark: Theme::dark_from_sources(
|
||||||
primary,
|
scheme.primary,
|
||||||
secondary,
|
scheme.secondary,
|
||||||
tertiary,
|
scheme.tertiary,
|
||||||
error,
|
scheme.error,
|
||||||
neutral,
|
scheme.neutral,
|
||||||
neutral_variant,
|
scheme.neutral_variant,
|
||||||
),
|
),
|
||||||
primary_fixed: FixedTheme::from_source(primary),
|
primary_fixed: FixedTheme::from_source(scheme.primary),
|
||||||
secondary_fixed: FixedTheme::from_source(secondary),
|
secondary_fixed: FixedTheme::from_source(scheme.secondary),
|
||||||
tertiary_fixed: FixedTheme::from_source(tertiary),
|
tertiary_fixed: FixedTheme::from_source(scheme.tertiary),
|
||||||
scrim: neutral.color(1),
|
scrim: scheme.neutral.color(1),
|
||||||
shadow: neutral.color(1),
|
shadow: scheme.neutral.color(1),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<ColorScheme> for ThemePair {
|
||||||
|
fn from(scheme: ColorScheme) -> Self {
|
||||||
|
Self::from_scheme(&scheme)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for ThemePair {
|
impl Default for ThemePair {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
const PRIMARY_HUE: f32 = -120.;
|
Self::from(ColorScheme::default())
|
||||||
const SECONDARY_HUE: f32 = 0.;
|
|
||||||
const TERTIARY_HUE: f32 = -30.;
|
|
||||||
const ERROR_HUE: f32 = 30.;
|
|
||||||
Self::from_sources(
|
|
||||||
ColorSource::new(PRIMARY_HUE, 0.8),
|
|
||||||
ColorSource::new(SECONDARY_HUE, 0.3),
|
|
||||||
ColorSource::new(TERTIARY_HUE, 0.3),
|
|
||||||
ColorSource::new(ERROR_HUE, 0.8),
|
|
||||||
ColorSource::new(0., 0.001),
|
|
||||||
ColorSource::new(30., 0.),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1177,7 +1165,7 @@ impl FixedTheme {
|
||||||
///
|
///
|
||||||
/// The goal of this type is to allow various tones of a given hue/saturation to
|
/// The goal of this type is to allow various tones of a given hue/saturation to
|
||||||
/// be generated easily.
|
/// be generated easily.
|
||||||
#[derive(Clone, Copy, Debug)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub struct ColorSource {
|
pub struct ColorSource {
|
||||||
/// A measurement of hue, in degees, from -180 to 180.
|
/// A measurement of hue, in degees, from -180 to 180.
|
||||||
///
|
///
|
||||||
|
|
@ -1238,7 +1226,7 @@ impl ColorSource {
|
||||||
} / 180.,
|
} / 180.,
|
||||||
);
|
);
|
||||||
|
|
||||||
saturation_delta * hue_delta
|
saturation_delta.one_minus() * hue_delta
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1574,3 +1562,154 @@ impl TryFrom<Component> for ContainerLevel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A builder of [`ColorScheme`]s.
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ColorSchemeBuilder {
|
||||||
|
/// The primary color of the scheme.
|
||||||
|
pub primary: ColorSource,
|
||||||
|
/// The secondary color of the scheme. If not provided, a complimentary
|
||||||
|
/// color will be chosen.
|
||||||
|
pub secondary: Option<ColorSource>,
|
||||||
|
/// The tertiary color of the scheme. If not provided, a complimentary
|
||||||
|
/// color will be chosen.
|
||||||
|
pub tertiary: Option<ColorSource>,
|
||||||
|
/// The error color of the scheme. If not provided, red will be used unless
|
||||||
|
/// it contrasts poorly with any of the other colors.
|
||||||
|
pub error: Option<ColorSource>,
|
||||||
|
/// The neutral color of the scheme. If not provided, a nearly fully
|
||||||
|
/// desaturated variation of the primary color will be used.
|
||||||
|
pub neutral: Option<ColorSource>,
|
||||||
|
/// The neutral variant color of the scheme. If not provided, a mostly
|
||||||
|
/// desaturated variation of the primary color will be used.
|
||||||
|
pub neutral_variant: Option<ColorSource>,
|
||||||
|
hue_shift: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorSchemeBuilder {
|
||||||
|
/// Returns a builder for the provided hue, in degrees.
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_hue(hue: impl Into<OklabHue>) -> Self {
|
||||||
|
Self::new(ColorSource::new(hue, 0.8))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a builder for the provided primary color.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(primary: ColorSource) -> Self {
|
||||||
|
Self {
|
||||||
|
primary,
|
||||||
|
secondary: None,
|
||||||
|
tertiary: None,
|
||||||
|
error: None,
|
||||||
|
neutral: None,
|
||||||
|
neutral_variant: None,
|
||||||
|
hue_shift: 30.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_secondary(&self) -> ColorSource {
|
||||||
|
ColorSource {
|
||||||
|
hue: self.primary.hue + self.hue_shift,
|
||||||
|
saturation: self.primary.saturation / 2.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_tertiary(&self, secondary: ColorSource) -> ColorSource {
|
||||||
|
let hue_shift = (secondary.hue - self.primary.hue).into_degrees().signum() * self.hue_shift;
|
||||||
|
ColorSource {
|
||||||
|
hue: self.primary.hue - hue_shift,
|
||||||
|
saturation: self.primary.saturation / 3.,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_error(&self, secondary: ColorSource, tertiary: ColorSource) -> ColorSource {
|
||||||
|
let mut error = ColorSource::new(30., self.primary.saturation);
|
||||||
|
while [self.primary, secondary, tertiary]
|
||||||
|
.iter()
|
||||||
|
.any(|c| c.contrast_between(error) < 0.10)
|
||||||
|
{
|
||||||
|
error.hue -= self.hue_shift;
|
||||||
|
}
|
||||||
|
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_neutral(&self) -> ColorSource {
|
||||||
|
ColorSource {
|
||||||
|
hue: self.primary.hue,
|
||||||
|
saturation: ZeroToOne::new(0.01),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate_neutral_variant(&self) -> ColorSource {
|
||||||
|
ColorSource {
|
||||||
|
hue: self.primary.hue,
|
||||||
|
saturation: ZeroToOne::new(0.1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a color scheme from the provided colors, generating any
|
||||||
|
/// unspecified colors.
|
||||||
|
#[must_use]
|
||||||
|
pub fn build(self) -> ColorScheme {
|
||||||
|
let secondary = self.secondary.unwrap_or_else(|| self.generate_secondary());
|
||||||
|
let tertiary = self
|
||||||
|
.tertiary
|
||||||
|
.unwrap_or_else(|| self.generate_tertiary(secondary));
|
||||||
|
ColorScheme {
|
||||||
|
primary: self.primary,
|
||||||
|
secondary,
|
||||||
|
tertiary,
|
||||||
|
error: self
|
||||||
|
.error
|
||||||
|
.unwrap_or_else(|| self.generate_error(secondary, tertiary)),
|
||||||
|
neutral: self.neutral.unwrap_or_else(|| self.generate_neutral()),
|
||||||
|
neutral_variant: self
|
||||||
|
.neutral_variant
|
||||||
|
.unwrap_or_else(|| self.generate_neutral_variant()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A color scheme for a Gooey application.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
pub struct ColorScheme {
|
||||||
|
/// The primary accent color.
|
||||||
|
pub primary: ColorSource,
|
||||||
|
/// A secondary accent color.
|
||||||
|
pub secondary: ColorSource,
|
||||||
|
/// A tertiary accent color.
|
||||||
|
pub tertiary: ColorSource,
|
||||||
|
/// A color used to denote errors.
|
||||||
|
pub error: ColorSource,
|
||||||
|
/// A neutral color.
|
||||||
|
pub neutral: ColorSource,
|
||||||
|
/// A neutral color with a different tone than `neutral`.
|
||||||
|
pub neutral_variant: ColorSource,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorScheme {
|
||||||
|
/// Returns a generated color scheme based on a `primary` color.
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_primary(primary: ColorSource) -> Self {
|
||||||
|
ColorSchemeBuilder::new(primary).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a generated color scheme based on a `primary` hue, in degrees.
|
||||||
|
#[must_use]
|
||||||
|
pub fn from_primary_hue(hue: impl Into<OklabHue>) -> Self {
|
||||||
|
ColorSchemeBuilder::from_hue(hue).build()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ColorScheme {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::from_primary_hue(138.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ColorSource> for ColorScheme {
|
||||||
|
fn from(primary: ColorSource) -> Self {
|
||||||
|
ColorScheme::from_primary(primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue