Derive theme gradients and add solid paint controls

This commit is contained in:
Samuel Onoja committed 2026-10-04 09:03:28 +01:00
1 parent 20395007a5
commit 831ac3a77f
18 files changed
+604 -20

No files matched your search

+8
View File
@@ -954,6 +954,14 @@ impl App {
fn change(&mut self, edit: Edit) -> Task<Message> {
match self.draft.edit(&edit) {
Ok(()) => {
// A shared style selection applies to both appearances while
// retaining their authored gradient endpoints.
for unset in visuals::paint_style_overrides(&self.draft, &edit) {
if self.draft.edit(&unset).is_ok() {
self.pending.push(unset);
}
}
// When a gradient exists, changing Accent also changes its
// leading stop; otherwise the visible focus border would stay
// on the old palette despite the control saying it changed.
+14
View File
@@ -742,6 +742,20 @@ pub fn fields(page: Page) -> Vec<Field> {
"Focus rings and selected controls. Use a hex color.",
"#3D7BE6",
),
choice(
"theme.focus_ring.style",
"Accent style",
"Window focus borders and selected controls. Gradients use the theme colors unless overridden.",
"auto",
&[("auto", "Gradient"), ("solid", "Solid")],
),
choice(
"theme.border.style",
"Inactive border style",
"Gradients retain the border color's transparency.",
"auto",
&[("auto", "Gradient"), ("solid", "Solid")],
),
choice(
"theme.material.style",
"Surface style",
+53
View File
@@ -137,6 +137,21 @@ pub fn toggle_split(snapshot: &Snapshot, enabled: bool, active: ferese_config::t
edits
}
pub fn paint_style_overrides(snapshot: &Snapshot, edit: &Edit) -> Vec<Edit> {
let Edit::Set(path, _) = edit else { return vec![] };
let section = match path.as_str() {
"theme.focus_ring.style" => "focus_ring",
"theme.border.style" => "border",
_ => return vec![],
};
["light", "dark"]
.into_iter()
.map(|kind| format!("theme.{kind}.{section}.style"))
.filter(|path| snapshot.item(path).is_some())
.map(Edit::Unset)
.collect()
}
#[cfg(test)]
pub fn preset(index: usize) -> Vec<Edit> {
let Some(preset) = PRESETS.get(index) else {
@@ -244,6 +259,44 @@ mod tests {
snapshot
}
#[test]
fn shared_style_controls_replace_scoped_styles_without_deleting_endpoints() {
let mut snapshot = Snapshot::parse(
r##"theme {
family catppuccin
light { focus-ring { style solid; gradient { from "#123456"; to "#654321"; }; }; }
dark { focus-ring { style solid; gradient { from "#ABCDEF"; to "#FEDCBA"; }; }; }
}"##
.into(),
)
.unwrap();
for (style, enabled) in [("auto", true), ("solid", false), ("auto", true)] {
let edit = set("theme.focus_ring.style", style);
for unset in paint_style_overrides(&snapshot, &edit) {
snapshot.edit(&unset).unwrap();
}
snapshot.edit(&edit).unwrap();
for (mode, from) in [("light", "#123456"), ("dark", "#ABCDEF")] {
snapshot.edit(&set("theme.mode", mode)).unwrap();
let theme = ferese_config::theme::resolve(
&snapshot.doc,
std::path::Path::new("/config"),
"2026-09-30T12:00:00Z".parse().unwrap(),
|_| unreachable!(),
)
.unwrap()
.theme;
assert_eq!(theme.tokens.focus_ring.gradient.is_some(), enabled);
if enabled {
assert_eq!(theme.tokens.focus_ring.gradient.unwrap().from, from);
}
assert!(snapshot.item(&format!("theme.{mode}.focus_ring.gradient")).is_some());
}
}
}
fn contrast(a: Color, b: Color) -> f32 {
ferese_theme::contrast(a, b)
}