Skip to content

Commit

Permalink
Diverging from material
Browse files Browse the repository at this point in the history
Introducing two new colors:

- ColorTheme::color_dim, for dimmed/disabled primary colors
- SurfaceTheme::opaque_widget, for buttons.

In material design, a button's background color uses the Highest
Container role, which seems incorrect because then buttons wouldn't have
a different color when placed inside of the highest level container.

Rather than remove a container level, I added one more tone using the
neutral variant.

Other changes are just gut feelings to have a slightly richer dark
theme. I feel like material is a little muddy in dark mode.
  • Loading branch information
ecton committed Nov 12, 2023
1 parent b3bef34 commit 849710d
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 17 deletions.
19 changes: 15 additions & 4 deletions examples/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,13 +187,13 @@ fn surface_theme(theme: Dynamic<SurfaceTheme>) -> impl MakeWidget {
Stack::columns(
swatch(color.clone(), "Surface", on_color.clone())
.and(swatch(
theme.map_each(|theme| theme.dim_color),
"Dim Surface",
theme.map_each(|theme| theme.bright_color),
"Bright Surface",
on_color.clone(),
))
.and(swatch(
theme.map_each(|theme| theme.bright_color),
"Bright Surface",
theme.map_each(|theme| theme.dim_color),
"Dim Surface",
on_color.clone(),
)),
)
Expand Down Expand Up @@ -245,6 +245,11 @@ fn surface_theme(theme: Dynamic<SurfaceTheme>) -> impl MakeWidget {
theme.map_each(|theme| theme.outline_variant),
"Outline Variant",
color,
))
.and(swatch(
theme.map_each(|theme| theme.opaque_widget),
"Opaque Widget",
on_color,
)),
)
.expand(),
Expand All @@ -255,11 +260,17 @@ fn surface_theme(theme: Dynamic<SurfaceTheme>) -> impl MakeWidget {

fn color_theme(theme: Dynamic<ColorTheme>, label: &str) -> impl MakeWidget {
let color = theme.map_each(|theme| theme.color);
let dim_color = theme.map_each(|theme| theme.color_dim);
let on_color = theme.map_each(|theme| theme.on_color);
let container = theme.map_each(|theme| theme.container);
let on_container = theme.map_each(|theme| theme.on_container);
Stack::rows(
swatch(color.clone(), label, on_color.clone())
.and(swatch(
dim_color.clone(),
&format!("{label} Dim"),
on_color.clone(),
))
.and(swatch(
on_color.clone(),
&format!("On {label}"),
Expand Down
29 changes: 19 additions & 10 deletions src/styles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,9 @@ pub struct SurfaceTheme {
/// The background color for highest-level container widgets.
pub highest_container: Color,

/// The default background color for widgets that are opaque.
pub opaque_widget: Color,

/// The default text/content color.
pub on_color: Color,
/// A variation of the text/content color that is de-emphasized.
Expand All @@ -1006,13 +1009,14 @@ impl SurfaceTheme {
#[must_use]
pub fn light_from_sources(neutral: ColorSource, neutral_variant: ColorSource) -> Self {
Self {
color: neutral.color(98),
dim_color: neutral_variant.color(70),
bright_color: neutral.color(100),
lowest_container: neutral.color(100),
low_container: neutral.color(96),
container: neutral.color(95),
high_container: neutral.color(90),
color: neutral.color(97),
dim_color: neutral.color(70),
bright_color: neutral.color(99),
opaque_widget: neutral_variant.color(75),
lowest_container: neutral.color(95),
low_container: neutral.color(92),
container: neutral.color(90),
high_container: neutral.color(85),
highest_container: neutral.color(80),
on_color: neutral.color(10),
on_color_variant: neutral_variant.color(30),
Expand All @@ -1027,8 +1031,9 @@ impl SurfaceTheme {
pub fn dark_from_sources(neutral: ColorSource, neutral_variant: ColorSource) -> Self {
Self {
color: neutral.color(10),
dim_color: neutral_variant.color(2),
dim_color: neutral.color(2),
bright_color: neutral.color(11),
opaque_widget: neutral_variant.color(40),
lowest_container: neutral.color(15),
low_container: neutral.color(20),
container: neutral.color(25),
Expand All @@ -1047,6 +1052,8 @@ impl SurfaceTheme {
pub struct ColorTheme {
/// The primary color, used for high-emphasis content.
pub color: Color,
/// The primary color, dimmed for de-emphasized or disabled content.
pub color_dim: Color,
/// The color for content that sits atop the primary color.
pub on_color: Color,
/// The backgrond color for containers.
Expand All @@ -1061,6 +1068,7 @@ impl ColorTheme {
pub fn light_from_source(source: ColorSource) -> Self {
Self {
color: source.color(40),
color_dim: source.color(30),
on_color: source.color(100),
container: source.color(90),
on_container: source.color(10),
Expand All @@ -1071,10 +1079,11 @@ impl ColorTheme {
#[must_use]
pub fn dark_from_source(source: ColorSource) -> Self {
Self {
color: source.color(80),
color: source.color(70),
color_dim: source.color(60),
on_color: source.color(10),
container: source.color(30),
on_container: source.color(80),
on_container: source.color(90),
}
}
}
Expand Down
19 changes: 19 additions & 0 deletions src/styles/components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,3 +476,22 @@ impl ComponentDefinition for DisabledOutlineColor {
context.theme().surface.outline_variant
}
}

/// A [`Color`] to be used as a background color for widgets that render an
/// opaque background.
#[derive(Clone, Copy, Eq, PartialEq, Debug)]
pub struct OpaqueWidgetColor;

impl NamedComponent for OpaqueWidgetColor {
fn name(&self) -> Cow<'_, ComponentName> {
Cow::Owned(ComponentName::named::<Global>("opaque_color"))
}
}

impl ComponentDefinition for OpaqueWidgetColor {
type ComponentType = Color;

fn default_value(&self, context: &WidgetContext<'_, '_>) -> Color {
context.theme().surface.opaque_widget
}
}
4 changes: 2 additions & 2 deletions src/widgets/button.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::animation::{AnimationHandle, AnimationTarget, Spawn};
use crate::context::{AsEventContext, EventContext, GraphicsContext, LayoutContext, WidgetContext};
use crate::names::Name;
use crate::styles::components::{
AutoFocusableControls, Easing, IntrinsicPadding, SurfaceColor, TextColor,
AutoFocusableControls, Easing, IntrinsicPadding, OpaqueWidgetColor, SurfaceColor, TextColor,
};
use crate::styles::{ColorExt, ComponentGroup, Styles};
use crate::utils::ModifiersExt;
Expand Down Expand Up @@ -323,7 +323,7 @@ impl ComponentGroup for Button {
define_components! {
Button {
/// The background color of the button.
ButtonBackground(Color, "background_color", .surface.highest_container) // TODO highest_container seems wrong, but it's what material uses. Perhaps we should add another color so that buttons don't blend with the highest container level.
ButtonBackground(Color, "background_color", |context| context.query_style(&OpaqueWidgetColor))
/// The background color of the button when it is active (depressed).
ButtonActiveBackground(Color, "active_background_color", .surface.color)
/// The background color of the button when the mouse cursor is hovering over
Expand Down
3 changes: 2 additions & 1 deletion src/widgets/slider.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use kludgine::{Color, Origin};

use crate::animation::{LinearInterpolate, PercentBetween};
use crate::context::{EventContext, GraphicsContext, LayoutContext, WidgetContext};
use crate::styles::components::OpaqueWidgetColor;
use crate::styles::{ComponentDefinition, ComponentName, Dimension, Group, NamedComponent};
use crate::value::{Dynamic, IntoDynamic, IntoValue, Value};
use crate::widget::{EventHandling, Widget, HANDLED};
Expand Down Expand Up @@ -406,7 +407,7 @@ impl ComponentDefinition for InactiveTrackColor {
type ComponentType = Color;

fn default_value(&self, context: &WidgetContext<'_, '_>) -> Self::ComponentType {
context.theme().surface.highest_container // TODO this is the same as ButtonBackground. This should be abstracted into its own component both can depend on.
context.query_style(&OpaqueWidgetColor)
}
}

Expand Down

0 comments on commit 849710d

Please sign in to comment.