Compare commits

...
Sign in to create a new pull request.

11 commits

Author SHA1 Message Date
Ashley Wulber
01ab456610 fix: blur only after blur event 2026-04-17 13:42:51 -04:00
Ashley Wulber
3ca50dd7f6 wip: corner radius v2 2026-04-16 23:02:39 -04:00
Ashley Wulber
b814f54f67 refactor opaque fallback 2026-04-15 15:06:07 -04:00
Ashley Wulber
9d51e8fda4 feat(blur): better align with designs and remove transparency from theme when not on wayland 2026-04-14 23:44:01 -04:00
Ashley Wulber
d04aa41d6a chore: update iced 2026-04-14 19:14:08 -04:00
Ashley Wulber
1b74c6f999 wip: blurred transparency 2026-04-13 11:04:55 -04:00
Ashley Wulber
ded784a4e3 chore: update qt light default kcolorscheme
this was likely off by one because of a rounding change from v1
2026-04-08 16:20:29 -04:00
Ashley Wulber
99e196cc79 chore: update ron files 2026-04-08 16:20:29 -04:00
Ashley Wulber
141bbd23ec feat: hex_color serialization for the theme
can also can deserialize the previous version of the theme, so existing themes should not be affected
2026-04-08 16:20:29 -04:00
Ashley Wulber
6653157def feat(config): support for intermediate serialization/deserialization type via field attribute 2026-04-08 16:20:29 -04:00
Ashley Wulber
bec679efc9 feat(config): support for fallback to previous config version 2026-04-08 16:20:29 -04:00
27 changed files with 1168 additions and 327 deletions

View file

@ -126,7 +126,7 @@ ashpd = { version = "0.12.3", default-features = false, optional = true }
async-fs = { version = "2.2", optional = true }
async-std = { version = "1.13", optional = true }
auto_enums = "0.8.8"
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086", optional = true }
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "a7d2d7a", optional = true }
jiff = "0.2"
cosmic-config = { path = "cosmic-config" }
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }

View file

@ -1,8 +1,8 @@
use proc_macro::TokenStream;
use quote::quote;
use syn::{self};
use syn;
#[proc_macro_derive(CosmicConfigEntry, attributes(version, id))]
#[proc_macro_derive(CosmicConfigEntry, attributes(version, id, cosmic_config_entry))]
pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
@ -12,6 +12,25 @@ pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream {
impl_cosmic_config_entry_macro(&ast)
}
fn get_cosmic_config_attrs(field: &syn::Field) -> Result<Option<syn::Type>, syn::Error> {
let mut with = None;
for attr in &field.attrs {
if !attr.path().is_ident("cosmic_config_entry") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("with") {
let value = meta.value()?;
with = Some(value.parse()?);
}
Ok(())
})?;
}
Ok(with)
}
fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
let attributes = &ast.attrs;
let version = attributes
@ -48,19 +67,54 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
let write_each_config_field = fields.iter().map(|field| {
let field_name = &field.ident;
quote! {
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
let with = match get_cosmic_config_attrs(field) {
Ok(attrs) => attrs,
Err(e) => {
return e.to_compile_error();
}
};
if let Some(with) = with {
quote! {
{
let conv = self.#field_name.clone().into();
cosmic_config::ConfigSet::set::<#with>(&tx, stringify!(#field_name), conv)?;
}
}
} else {
quote! {
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
}
}
});
let get_each_config_field = fields.iter().map(|field| {
let field_name = &field.ident;
let field_type = &field.ty;
quote! {
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
Ok(#field_name) => default.#field_name = #field_name,
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
Err(e) => errors.push(e),
let with = match get_cosmic_config_attrs(field) {
Ok(attrs) => attrs,
Err(e) => {
return e.to_compile_error();
}
};
if let Some(with) = with {
quote! {
match cosmic_config::ConfigGet::get::<#with>(config, stringify!(#field_name)) {
Ok(value) => {
default.#field_name = value.into();
}
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
Err(e) => errors.push(e),
}
}
} else {
quote! {
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
Ok(#field_name) => default.#field_name = #field_name,
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
Err(e) => errors.push(e),
}
}
}
});
@ -68,17 +122,39 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
let update_each_config_field = fields.iter().map(|field| {
let field_name = &field.ident;
let field_type = &field.ty;
quote! {
stringify!(#field_name) => {
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
Ok(value) => {
if self.#field_name != value {
keys.push(stringify!(#field_name));
}
self.#field_name = value;
},
Err(e) => {
errors.push(e);
let with = match get_cosmic_config_attrs(field) {
Ok(attrs) => attrs,
Err(e) => {
return e.to_compile_error();
}
};
if let Some(with) = with {
quote! {
stringify!(#field_name) => {
match cosmic_config::ConfigGet::get::<#with>(config, stringify!(#field_name)) {
Ok(value) => {
let value = value.into();
if self.#field_name != value {
keys.push(stringify!(#field_name));
}
self.#field_name = value;
},
Err(e) => errors.push(e),
}
}
}
} else {
quote! {
stringify!(#field_name) => {
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
Ok(value) => {
if self.#field_name != value {
keys.push(stringify!(#field_name));
}
self.#field_name = value;
},
Err(e) => errors.push(e),
}
}
}

View file

@ -162,6 +162,7 @@ pub trait ConfigSet {
pub struct Config {
system_path: Option<PathBuf>,
user_path: Option<PathBuf>,
previous: Option<Box<Config>>,
}
/// Check that the name is relative and doesn't contain . or ..
@ -180,9 +181,13 @@ fn sanitize_name(name: &str) -> Result<&Path, Error> {
impl Config {
/// Get a system config for the given name and config version
pub fn system(name: &str, version: u64) -> Result<Self, Error> {
Self::system_inner(name, version, true)
}
fn system_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
let path = sanitize_name(name)?.join(format!("v{version}"));
#[cfg(unix)]
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(path);
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(&path);
#[cfg(windows)]
let system_path =
@ -192,6 +197,13 @@ impl Config {
Ok(Self {
system_path,
user_path: None,
previous: if version > 1 && look_for_previous {
Self::system_inner(name, version - 1, false)
.ok()
.map(Box::new)
} else {
None
},
})
}
@ -199,6 +211,10 @@ impl Config {
// Use folder at XDG config/name for config storage, return Config if successful
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
pub fn new(name: &str, version: u64) -> Result<Self, Error> {
Self::new_inner(name, version, true)
}
fn new_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
// Look for [name]/v[version]
let path = sanitize_name(name)?.join(format!("v{}", version));
@ -223,15 +239,29 @@ impl Config {
Ok(Self {
system_path,
user_path: Some(user_path),
previous: if version > 1 && look_for_previous {
Self::new_inner(name, version - 1, false).ok().map(Box::new)
} else {
None
},
})
}
/// Get config for the given application name and config version and custom path.
pub fn with_custom_path(name: &str, version: u64, custom_path: PathBuf) -> Result<Self, Error> {
Self::with_custom_path_inner(name, version, custom_path, true)
}
fn with_custom_path_inner(
name: &str,
version: u64,
custom_path: PathBuf,
look_for_previous: bool,
) -> Result<Self, Error> {
// Look for [name]/v[version]
let path = sanitize_name(name)?.join(format!("v{version}"));
let mut user_path = custom_path;
let mut user_path = custom_path.clone();
user_path.push("cosmic");
user_path.push(path);
// Create new configuration directory if not found.
@ -241,6 +271,13 @@ impl Config {
Ok(Self {
system_path: None,
user_path: Some(user_path),
previous: if version > 1 && look_for_previous {
Self::with_custom_path_inner(name, version - 1, custom_path.clone(), false)
.ok()
.map(Box::new)
} else {
None
},
})
}
@ -250,6 +287,10 @@ impl Config {
// Use folder at XDG config/name for config storage, return Config if successful
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
Self::new_state_inner(name, version, true)
}
fn new_state_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
// Look for [name]/v[version]
let path = sanitize_name(name)?.join(format!("v{}", version));
@ -263,6 +304,13 @@ impl Config {
Ok(Self {
system_path: None,
user_path: Some(user_path),
previous: if version > 1 && look_for_previous {
Self::new_state_inner(name, version - 1, false)
.ok()
.map(Box::new)
} else {
None
},
})
}
@ -373,7 +421,13 @@ impl ConfigGet for Config {
Ok(ron::from_str(&data)?)
}
_ => Err(Error::NotFound),
_ => {
if let Some(previous) = self.previous.as_ref() {
previous.get_local(key)
} else {
Err(Error::NotFound)
}
}
}
}

View file

@ -15,6 +15,7 @@ export = ["serde_json"]
no-default = []
[dependencies]
hex_color = { version = "3", features = ["serde"] }
palette = { version = "0.7.6", features = ["serializing"] }
almost = "0.2"
serde = { version = "1.0.228", features = ["derive"] }

View file

@ -0,0 +1,173 @@
//! Color representation and serde helpers for the Cosmic theme
use hex_color::HexColor;
use palette::{Srgb, Srgba};
use serde::{Deserialize, Serialize};
/// A color in the Cosmic theme for serialization and deserialization
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
#[serde(untagged)]
pub enum ColorRepr {
/// A color represented as a hex string
#[serde(with = "hex_color::rgba")]
Hex(HexColor),
/// A color represented as an RGBA value
Rgba(Srgba),
/// A color represented as an RGB value
Rgb(Srgb),
}
/// An optional color in the Cosmic theme for serialization and deserialization
#[repr(transparent)]
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
#[serde(transparent)]
pub struct ColorReprOption(Option<ColorRepr>);
impl From<Srgb> for ColorRepr {
fn from(color: Srgb) -> Self {
let rgb_u8: Srgb<u8> = color.into_format();
ColorRepr::Hex(HexColor {
r: rgb_u8.red,
g: rgb_u8.green,
b: rgb_u8.blue,
a: 255,
})
}
}
impl From<Srgba> for ColorRepr {
fn from(color: Srgba) -> Self {
let rgba_u8: Srgba<u8> = color.into_format();
ColorRepr::Hex(HexColor {
r: rgba_u8.red,
g: rgba_u8.green,
b: rgba_u8.blue,
a: rgba_u8.alpha,
})
}
}
impl From<ColorRepr> for Srgb {
fn from(value: ColorRepr) -> Self {
match value {
ColorRepr::Hex(hex) => Srgb::<u8>::new(hex.r, hex.g, hex.b).into_format(),
ColorRepr::Rgb(rgb) => rgb,
ColorRepr::Rgba(rgba) => Srgb::new(rgba.red, rgba.green, rgba.blue),
}
}
}
impl From<ColorRepr> for Srgba {
fn from(value: ColorRepr) -> Self {
match value {
ColorRepr::Hex(hex) => Srgba::<u8>::new(hex.r, hex.g, hex.b, hex.a).into_format(),
ColorRepr::Rgb(rgb) => Srgba::new(rgb.red, rgb.green, rgb.blue, 1.0),
ColorRepr::Rgba(rgba) => rgba,
}
}
}
impl From<ColorReprOption> for Option<Srgb> {
fn from(value: ColorReprOption) -> Self {
value.0.map(std::convert::Into::into)
}
}
impl From<ColorReprOption> for Option<Srgba> {
fn from(value: ColorReprOption) -> Self {
value.0.map(std::convert::Into::into)
}
}
impl From<Option<Srgb>> for ColorReprOption {
fn from(value: Option<Srgb>) -> Self {
ColorReprOption(value.map(std::convert::Into::into))
}
}
impl From<Option<Srgba>> for ColorReprOption {
fn from(value: Option<Srgba>) -> Self {
ColorReprOption(value.map(std::convert::Into::into))
}
}
/// A trait for converting between a color type and its representation for serialization and deserialization
pub trait ConvColorRepr: Sized {
/// Convert from a color representation to the color type
fn from_repr(repr: ColorRepr) -> Self;
/// Convert from the color type to its representation for serialization
fn to_repr(&self) -> ColorRepr;
}
impl ConvColorRepr for Srgba {
fn from_repr(repr: ColorRepr) -> Self {
repr.into()
}
fn to_repr(&self) -> ColorRepr {
(*self).into()
}
}
impl ConvColorRepr for Srgb {
fn from_repr(repr: ColorRepr) -> Self {
repr.into()
}
fn to_repr(&self) -> ColorRepr {
(*self).into()
}
}
/// Serde helpers for serializing and deserializing colors in the Cosmic theme
pub mod color_serde {
use super::*;
use serde::{Deserialize, Deserializer, Serializer};
/// Serialize a color to a hex string
pub fn serialize<T, S>(color: &T, serializer: S) -> Result<S::Ok, S::Error>
where
T: ConvColorRepr,
S: Serializer,
{
let repr = color.to_repr();
repr.serialize(serializer)
}
/// Deserialize a color from a hex string or RGB/RGBA
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
where
T: ConvColorRepr,
D: Deserializer<'de>,
{
let repr = ColorRepr::deserialize(deserializer)?;
Ok(T::from_repr(repr))
}
/// Serde helpers for serializing and deserializing optional colors in the Cosmic theme
pub mod option {
use super::*;
/// Serialize an optional color
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
where
T: ConvColorRepr,
S: Serializer,
{
match value {
Some(v) => super::serialize(v, serializer),
None => serializer.serialize_none(),
}
}
/// Deserialize an optional color
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
T: ConvColorRepr,
D: Deserializer<'de>,
{
let opt = Option::<ColorRepr>::deserialize(deserializer)?;
Ok(opt.map(T::from_repr))
}
}
}

View file

@ -1,3 +1,4 @@
use crate::color::color_serde;
use palette::Srgba;
use serde::{Deserialize, Serialize};
use std::sync::LazyLock;
@ -95,75 +96,107 @@ pub struct CosmicPaletteInner {
/// Utility Colors
/// Colors used for various points of emphasis in the UI.
#[serde(with = "color_serde")]
pub bright_red: Srgba,
/// Colors used for various points of emphasis in the UI.
#[serde(with = "color_serde")]
pub bright_green: Srgba,
/// Colors used for various points of emphasis in the UI.
#[serde(with = "color_serde")]
pub bright_orange: Srgba,
/// Surface Grays
/// Colors used for three levels of surfaces in the UI.
#[serde(with = "color_serde")]
pub gray_1: Srgba,
/// Colors used for three levels of surfaces in the UI.
#[serde(with = "color_serde")]
pub gray_2: Srgba,
/// System Neutrals
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_0: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_1: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_2: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_3: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_4: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_5: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_6: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_7: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_8: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_9: Srgba,
/// A wider spread of dark colors for more general use.
#[serde(with = "color_serde")]
pub neutral_10: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_blue: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_indigo: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_purple: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_pink: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_red: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_orange: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_yellow: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_green: Srgba,
/// Potential Accent Color Combos
#[serde(with = "color_serde")]
pub accent_warm_grey: Srgba,
/// Extended Color Palette
/// Colors used for themes, app icons, illustrations, and other brand purposes.
#[serde(with = "color_serde")]
pub ext_warm_grey: Srgba,
/// Colors used for themes, app icons, illustrations, and other brand purposes.
#[serde(with = "color_serde")]
pub ext_orange: Srgba,
/// Colors used for themes, app icons, illustrations, and other brand purposes.
#[serde(with = "color_serde")]
pub ext_yellow: Srgba,
/// Colors used for themes, app icons, illustrations, and other brand purposes.
#[serde(with = "color_serde")]
pub ext_blue: Srgba,
/// Colors used for themes, app icons, illustrations, and other brand purposes.
#[serde(with = "color_serde")]
pub ext_purple: Srgba,
/// Colors used for themes, app icons, illustrations, and other brand purposes.
#[serde(with = "color_serde")]
pub ext_pink: Srgba,
/// Colors used for themes, app icons, illustrations, and other brand purposes.
#[serde(with = "color_serde")]
pub ext_indigo: Srgba,
}

View file

@ -1 +1 @@
Dark((name:"cosmic-dark",bright_red:(red:1.0,green:0.62745098,blue:0.60392157,alpha:1.0),bright_green:(red:0.36862745,green:0.85882352,blue:0.54901960,alpha:1.0),bright_orange:(red:1.0,green:0.63921569,blue:0.49019608,alpha:1.0),gray_1:(red:0.10588235,green:0.10588235,blue:0.10588235,alpha:1.0),gray_2:(red:0.14901961,green:0.14901961,blue:0.14901961,alpha:1.0),neutral_0:(red:0.0,green:0.0,blue:0.0,alpha:1.0),neutral_1:(red:0.01176471,green:0.01176471,blue:0.01176471,alpha:1.0),neutral_2:(red:0.08627451,green:0.08627451,blue:0.08627451,alpha:1.0),neutral_3:(red:0.18039216,green:0.18039216,blue:0.18039216,alpha:1.0),neutral_4:(red:0.28235294,green:0.28235294,blue:0.28235294,alpha:1.0),neutral_5:(red:0.38823529,green:0.38823529,blue:0.38823529,alpha:1.0),neutral_6:(red:0.50196078,green:0.50196078,blue:0.50196078,alpha:1.0),neutral_7:(red:0.61960784,green:0.61960784,blue:0.61960784,alpha:1.0),neutral_8:(red:0.74509804,green:0.74509804,blue:0.74509804,alpha:1.0),neutral_9:(red:0.87058824,green:0.87058824,blue:0.87058824,alpha:1.0),neutral_10:(red:1.0,green:1.0,blue:1.0,alpha:1.0),accent_blue:(red:0.3882353,green:0.81568627,blue:0.87450981,alpha:1.0),accent_indigo:(red:0.63137255,green:0.75294118,blue:0.92156863,alpha:1.0),accent_purple:(red:0.90588235,green:0.61176471,blue:0.99607843,alpha:1.0),accent_pink:(red:1.0,green:0.61176471,blue:0.69411765,alpha:1.0),accent_red:(red:0.99215686,green:0.63137255,blue:0.62745098,alpha:1.0),accent_orange:(red:1.0,green:0.67843137,blue:0.0,alpha:1.0),accent_yellow:(red:0.96862745,green:0.87843137,blue:0.38431373,alpha:1.0),accent_green:(red:0.57254902,green:0.81176471,blue:0.61176471,alpha:1.0),accent_warm_grey:(red:0.79215686,green:0.72941176,blue:0.70588235,alpha:1.0),ext_warm_grey:(red:0.60784314,green:0.55686275,blue:0.54117647,alpha:1.0),ext_orange:(red:1.0,green:0.67843137,blue:0.0,alpha:1.0),ext_yellow:(red:0.99607843,green:0.85882353,blue:0.25098039,alpha:1.0),ext_blue:(red:0.28235294,green:0.72549020,blue:0.78039216,alpha:1.0),ext_purple:(red:0.81176471,green:0.49019608,blue:1.0,alpha:1.0),ext_pink:(red:0.97647059,green:0.22745098,blue:0.51372549,alpha:1.0),ext_indigo:(red:0.24313725,green:0.53333333,blue:1.0,alpha:1.0)))
Dark((name: "cosmic-dark",bright_red: "#FFA09AFF",bright_green: "#5EDB8CFF",bright_orange: "#FFA37DFF",gray_1: "#1B1B1BFF",gray_2: "#262626FF",neutral_0: "#000000FF",neutral_1: "#030303FF",neutral_2: "#161616FF",neutral_3: "#2E2E2EFF",neutral_4: "#484848FF",neutral_5: "#636363FF",neutral_6: "#808080FF",neutral_7: "#9E9E9EFF",neutral_8: "#BEBEBEFF",neutral_9: "#DEDEDEFF",neutral_10: "#FFFFFFFF",accent_blue: "#63D0DFFF",accent_indigo: "#A1C0EBFF",accent_purple: "#E79CFEFF",accent_pink: "#FF9CB1FF",accent_red: "#FDA1A0FF",accent_orange: "#FFAD00FF",accent_yellow: "#F7E062FF",accent_green: "#92CF9CFF",accent_warm_grey: "#CABAB4FF",ext_warm_grey: "#9B8E8AFF",ext_orange: "#FFAD00FF",ext_yellow: "#FEDB40FF",ext_blue: "#48B9C7FF",ext_purple: "#CF7DFFFF",ext_pink: "#F93A83FF",ext_indigo: "#3E88FFFF",))

View file

@ -1,3 +1,4 @@
use crate::color::color_serde;
use palette::{Srgba, WithAlpha};
use serde::{Deserialize, Serialize};
@ -8,14 +9,18 @@ use crate::composite::over;
#[must_use]
pub struct Container {
/// the color of the container
#[serde(with = "color_serde")]
pub base: Srgba,
/// the color of components in the container
pub component: Component,
/// the color of dividers in the container
#[serde(with = "color_serde")]
pub divider: Srgba,
/// the color of text in the container
#[serde(with = "color_serde")]
pub on: Srgba,
/// the color of @small_widget_container
#[serde(with = "color_serde")]
pub small_widget: Srgba,
}
@ -45,30 +50,42 @@ impl Container {
#[must_use]
pub struct Component {
/// The base color of the widget
#[serde(with = "color_serde")]
pub base: Srgba,
/// The color of the widget when it is hovered
#[serde(with = "color_serde")]
pub hover: Srgba,
/// the color of the widget when it is pressed
#[serde(with = "color_serde")]
pub pressed: Srgba,
/// the color of the widget when it is selected
#[serde(with = "color_serde")]
pub selected: Srgba,
/// the color of the widget when it is selected
#[serde(with = "color_serde")]
pub selected_text: Srgba,
/// the color of the widget when it is focused
#[serde(with = "color_serde")]
pub focus: Srgba,
/// the color of dividers for this widget
#[serde(with = "color_serde")]
pub divider: Srgba,
/// the color of text for this widget
#[serde(with = "color_serde")]
pub on: Srgba,
// the color of text with opacity 80 for this widget
// pub text_opacity_80: Srgba,
/// the color of the widget when it is disabled
#[serde(with = "color_serde")]
pub disabled: Srgba,
/// the color of text in the widget when it is disabled
#[serde(with = "color_serde")]
pub on_disabled: Srgba,
/// the color of the border for the widget
#[serde(with = "color_serde")]
pub border: Srgba,
/// the color of the border for the widget when it is disabled
#[serde(with = "color_serde")]
pub disabled_border: Srgba,
}

View file

@ -1 +1 @@
Light((name:"cosmic-light",bright_red:(red:0.53725490,green:0.01568627,blue:0.09411765,alpha:1.0),bright_green:(red:0.0,green:0.34117647,blue:0.17254901,alpha:1.0),bright_orange:(red:0.47450980,green:0.17254902,blue:0.0,alpha:1.0),gray_1:(red:0.84313725,green:0.84313725,blue:0.84313725,alpha:1.0),gray_2:(red:0.89411765,green:0.89411765,blue:0.89411765,alpha:1.0),neutral_0:(red:1.0,green:1.0,blue:1.0,alpha:1.0),neutral_1:(red:0.87058824,green:0.87058824,blue:0.87058824,alpha:1.0),neutral_2:(red:0.74509804,green:0.74509804,blue:0.74509804,alpha:1.0),neutral_3:(red:0.61960784,green:0.61960784,blue:0.61960784,alpha:1.0),neutral_4:(red:0.50196078,green:0.50196078,blue:0.50196078,alpha:1.0),neutral_5:(red:0.38823529,green:0.38823529,blue:0.38823529,alpha:1.0),neutral_6:(red:0.28235294,green:0.28235294,blue:0.28235294,alpha:1.0),neutral_7:(red:0.18039216,green:0.18039216,blue:0.18039216,alpha:1.0),neutral_8:(red:0.08627451,green:0.08627451,blue:0.08627451,alpha:1.0),neutral_9:(red:0.01176471,green:0.01176471,blue:0.01176471,alpha:1.0),neutral_10:(red:0.0,green:0.0,blue:0.0,alpha:1.0),accent_blue:(red:0.0,green:0.32156863,blue:0.35294118,alpha:1.0),accent_indigo:(red:0.18039216,green:0.28627451,blue:0.42745098,alpha:1.0),accent_purple:(red:0.40784314,green:0.12941176,blue:0.48627451,alpha:1.0),accent_pink:(red:0.52549020,green:0.01568627,blue:0.22745098,alpha:1.0),accent_red:(red:0.47058824,green:0.16078431,blue:0.18039216,alpha:1.0),accent_orange:(red:0.38431373,green:0.25098039,blue:0.0,alpha:1.0),accent_yellow:(red:0.32549020,green:0.28235294,blue:0.0,alpha:1.0),accent_green:(red:0.09411765,green:0.33333333,blue:0.16078431,alpha:1.0),accent_warm_grey:(red:0.33333333,green:0.27843137,blue:0.25882353,alpha:1.0),ext_warm_grey:(red:0.60784314,green:0.55686275,blue:0.54117647,alpha:1.0),ext_orange:(red:0.98431373,green:0.72156863,blue:0.42352941,alpha:1.0),ext_yellow:(red:0.96862745,green:0.87843137,blue:0.38431373,alpha:1.0),ext_blue:(red:0.41568627,green:0.79215686,blue:0.84705882,alpha:1.0),ext_purple:(red:0.83529412,green:0.54901961,blue:1.0,alpha:1.0),ext_pink:(red:1.0,green:0.61176471,blue:0.86666667,alpha:1.0),ext_indigo:(red:0.58431373,green:0.76862745,blue:0.98823529,alpha:1.0)))
Light((name: "cosmic-light",bright_red: "#890418FF",bright_green: "#00572CFF",bright_orange: "#792C00FF",gray_1: "#D7D7D7FF",gray_2: "#E4E4E4FF",neutral_0: "#FFFFFFFF",neutral_1: "#DEDEDEFF",neutral_2: "#BEBEBEFF",neutral_3: "#9E9E9EFF",neutral_4: "#808080FF",neutral_5: "#636363FF",neutral_6: "#484848FF",neutral_7: "#2E2E2EFF",neutral_8: "#161616FF",neutral_9: "#030303FF",neutral_10: "#000000FF",accent_blue: "#00525AFF",accent_indigo: "#2E496DFF",accent_purple: "#68217CFF",accent_pink: "#86043AFF",accent_red: "#78292EFF",accent_orange: "#624000FF",accent_yellow: "#534800FF",accent_green: "#185529FF",accent_warm_grey: "#554742FF",ext_warm_grey: "#9B8E8AFF",ext_orange: "#FBB86CFF",ext_yellow: "#F7E062FF",ext_blue: "#6ACAD8FF",ext_purple: "#D58CFFFF",ext_pink: "#FF9CDDFF",ext_indigo: "#95C4FCFF",))

View file

@ -6,6 +6,7 @@ pub use mode::*;
pub use spacing::*;
pub use theme::*;
pub mod color;
mod corner;
mod cosmic_palette;
mod density;

View file

@ -1,15 +1,16 @@
use crate::{
Component, Container, CornerRadii, CosmicPalette, CosmicPaletteInner, DARK_PALETTE,
LIGHT_PALETTE, NAME, Spacing, ThemeMode,
color::{ColorRepr, ColorReprOption, color_serde, color_serde::option as color_serde_option},
composite::over,
steps::{color_index, get_small_widget_color, get_surface_color, get_text, steps},
};
use cosmic_config::{Config, CosmicConfigEntry};
use cosmic_config::{Config, CosmicConfigEntry, cosmic_config_derive::CosmicConfigEntry};
use palette::{
IntoColor, Oklcha, Srgb, Srgba, WithAlpha, color_difference::Wcag21RelativeContrast, rgb::Rgb,
};
use serde::{Deserialize, Serialize};
use std::num::NonZeroUsize;
use std::{default, num::NonZeroUsize};
/// ID for the current dark `ThemeBuilder` config
pub const DARK_THEME_BUILDER_ID: &str = "com.system76.CosmicTheme.Dark.Builder";
@ -37,24 +38,25 @@ pub enum Layer {
#[must_use]
/// Cosmic Theme data structure with all colors and its name
#[derive(
Clone,
Debug,
Serialize,
Deserialize,
PartialEq,
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
)]
#[version = 1]
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, CosmicConfigEntry)]
#[version = 2]
pub struct Theme {
/// name of the theme
pub name: String,
/// background element colors
pub background: Container,
pub(crate) background: Container,
/// primary element colors
pub primary: Container,
pub(crate) primary: Container,
/// secondary element colors
pub secondary: Container,
pub(crate) secondary: Container,
/// background element colors
pub(crate) transparent_background: Container,
/// primary element colors
pub(crate) transparent_primary: Container,
/// secondary element colors
pub(crate) transparent_secondary: Container,
/// button component styling
pub button: Component,
/// accent element colors
pub accent: Component,
/// suggested element colors
@ -77,8 +79,6 @@ pub struct Theme {
pub link_button: Component,
/// text button element colors
pub text_button: Component,
/// button component styling
pub button: Component,
/// palette
pub palette: CosmicPaletteInner,
/// spacing
@ -96,15 +96,31 @@ pub struct Theme {
/// cosmic-comp custom window hint color
pub window_hint: Option<Srgb>,
/// enables blurred transparency
pub is_frosted: bool,
pub frosted: BlurStrength,
/// frosted windows
pub frosted_windows: bool,
/// frosted system interface
pub frosted_system_interface: bool,
/// frosted panel
pub frosted_panel: bool,
/// frosted applet popups
pub frosted_applets: bool,
/// shade color for dialogs
#[serde(with = "color_serde")]
#[cosmic_config_entry(with = ColorRepr)]
pub shade: Srgba,
/// accent text colors
/// If None, accent base color is the accent text color.
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub accent_text: Option<Srgba>,
/// control tint color
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub control_tint: Option<Srgb>,
/// text tint color
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub text_tint: Option<Srgb>,
}
@ -170,6 +186,39 @@ impl Theme {
todo!();
}
#[allow(clippy::doc_markdown)]
#[inline]
/// get opaque or transparent background based on whether blur is active
pub fn background(&self, transparent: bool) -> &Container {
if transparent {
&self.transparent_background
} else {
&self.background
}
}
#[allow(clippy::doc_markdown)]
#[inline]
/// get opaque or transparent primary based on whether blur is active
pub fn primary(&self, transparent: bool) -> &Container {
if transparent {
&self.transparent_primary
} else {
&self.primary
}
}
#[allow(clippy::doc_markdown)]
#[inline]
/// get opaque or transparent secondary based on whether blur is active
pub fn secondary(&self, transparent: bool) -> &Container {
if transparent {
&self.transparent_secondary
} else {
&self.secondary
}
}
#[must_use]
#[allow(clippy::doc_markdown)]
#[inline]
@ -739,7 +788,7 @@ impl Theme {
if color_scheme.trim().contains("default") || color_scheme.trim().contains("light") {
return Self::light_default();
}
};
}
Self::dark_default()
}
@ -748,10 +797,10 @@ impl Theme {
pub fn preferred_theme() -> Self {
let current_desktop = std::env::var("XDG_CURRENT_DESKTOP");
if let Ok(desktop) = current_desktop {
if desktop.trim().to_lowercase().contains("gnome") {
return Self::gtk_prefer_colorscheme();
}
if let Ok(desktop) = current_desktop
&& desktop.trim().to_lowercase().contains("gnome")
{
return Self::gtk_prefer_colorscheme();
}
Self::dark_default()
@ -766,15 +815,8 @@ impl From<CosmicPalette> for Theme {
#[must_use]
/// Helper for building customized themes
#[derive(
Clone,
Debug,
Serialize,
Deserialize,
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
PartialEq,
)]
#[version = 1]
#[derive(Clone, Debug, Serialize, Deserialize, CosmicConfigEntry, PartialEq)]
#[version = 2]
pub struct ThemeBuilder {
/// override the palette for the builder
pub palette: CosmicPalette,
@ -783,31 +825,59 @@ pub struct ThemeBuilder {
/// override corner radii for the builder
pub corner_radii: CornerRadii,
/// override neutral_tint for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub neutral_tint: Option<Srgb>,
/// override bg_color for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub bg_color: Option<Srgba>,
/// override the primary container bg color for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub primary_container_bg: Option<Srgba>,
/// override the secontary container bg color for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub secondary_container_bg: Option<Srgba>,
/// override the text tint for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub text_tint: Option<Srgb>,
/// override the accent color for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub accent: Option<Srgb>,
/// override the success color for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub success: Option<Srgb>,
/// override the warning color for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub warning: Option<Srgb>,
/// override the destructive color for the builder
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub destructive: Option<Srgb>,
/// enabled blurred transparency
pub is_frosted: bool, // TODO handle
pub frosted: BlurStrength,
/// cosmic-comp window gaps size (outer, inner)
pub gaps: (u32, u32),
/// cosmic-comp active hint window outline width
pub active_hint: u32,
/// cosmic-comp custom window hint color
#[serde(with = "color_serde_option")]
#[cosmic_config_entry(with = ColorReprOption)]
pub window_hint: Option<Srgb>,
/// frosted windows
pub frosted_windows: bool,
/// frosted system interface
pub frosted_system_interface: bool,
/// frosted panel
pub frosted_panel: bool,
/// frosted applet popups
pub frosted_applets: bool,
}
impl Default for ThemeBuilder {
@ -825,11 +895,15 @@ impl Default for ThemeBuilder {
success: Default::default(),
warning: Default::default(),
destructive: Default::default(),
is_frosted: false,
frosted: BlurStrength::default(),
// cosmic-comp theme settings
gaps: (0, 8),
active_hint: 3,
window_hint: None,
frosted_windows: false,
frosted_system_interface: false,
frosted_panel: false,
frosted_applets: false,
}
}
}
@ -971,9 +1045,15 @@ impl ThemeBuilder {
gaps,
active_hint,
window_hint,
is_frosted,
frosted,
frosted_windows,
frosted_system_interface,
frosted_panel,
frosted_applets,
} = self;
let container_alpha = frosted.alpha();
let is_dark = palette.is_dark();
let is_high_contrast = palette.is_high_contrast();
@ -1028,6 +1108,10 @@ impl ThemeBuilder {
let step_array = steps(bg, NonZeroUsize::new(100).unwrap());
let bg_index = color_index(bg, step_array.len());
let mut transparent_bg = bg;
transparent_bg.alpha = container_alpha;
let transparent_bg_steps_array = steps(transparent_bg, NonZeroUsize::new(100).unwrap());
let mut component_hovered_overlay = if bg_index < 91 {
control_steps_array[10]
} else {
@ -1054,8 +1138,22 @@ impl ThemeBuilder {
text_steps_array.as_deref(),
);
let transparent_bg_component = get_surface_color(
bg_index,
8,
&transparent_bg_steps_array,
is_dark,
&p_ref.neutral_2,
);
let on_transparent_bg_component = get_text(
color_index(transparent_bg_component, transparent_bg_steps_array.len()),
&transparent_bg_steps_array,
&control_steps_array[8],
text_steps_array.as_deref(),
);
let primary = {
let container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
let mut container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
primary_container_bg_color
} else {
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
@ -1102,6 +1200,45 @@ impl ThemeBuilder {
is_high_contrast,
)
};
let transparent_primary = {
let mut container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
primary_container_bg_color
} else {
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
};
container_bg.alpha = (container_alpha + if is_dark { 0.3 } else { 0.25 }).min(1.0);
let step_array = steps(container_bg, NonZeroUsize::new(100).unwrap());
let base_index: usize = color_index(container_bg, step_array.len());
let component_base =
get_surface_color(base_index, 6, &step_array, is_dark, &control_steps_array[3]);
Container::new(
Component::component(
component_base,
accent,
get_text(
color_index(component_base, step_array.len()),
&step_array,
&control_steps_array[8],
text_steps_array.as_deref(),
),
Srgba::new(0., 0., 0., 0.0),
Srgba::new(0., 0., 0., 0.0),
is_high_contrast,
control_steps_array[8],
),
container_bg,
get_text(
base_index,
&step_array,
&control_steps_array[8],
text_steps_array.as_deref(),
),
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
is_high_contrast,
)
};
let accent_text = if is_dark {
(primary.base.relative_contrast(accent.color) < 4.).then(|| {
@ -1332,10 +1469,75 @@ impl ThemeBuilder {
gaps,
active_hint,
window_hint,
is_frosted,
frosted,
accent_text,
control_tint: neutral_tint,
text_tint,
frosted_windows,
frosted_system_interface,
frosted_panel,
frosted_applets,
transparent_background: Container::new(
Component::component(
transparent_bg_component,
accent,
on_transparent_bg_component,
Srgba::new(0., 0., 0., 0.0),
Srgba::new(0., 0., 0., 0.0),
is_high_contrast,
control_steps_array[8],
),
transparent_bg,
get_text(
bg_index,
&transparent_bg_steps_array,
&control_steps_array[8],
text_steps_array.as_deref(),
),
get_small_widget_color(bg_index, 5, &neutral_steps, &control_steps_array[6]),
is_high_contrast,
),
transparent_primary,
transparent_secondary: {
let mut container_bg = if let Some(secondary_container_bg) = secondary_container_bg
{
secondary_container_bg
} else {
get_surface_color(bg_index, 10, &step_array, is_dark, &control_steps_array[2])
};
container_bg.alpha = (container_alpha + if is_dark { 0.6 } else { 0.5 }).min(1.0);
let step_array = steps(container_bg, NonZeroUsize::new(100).unwrap());
let base_index = color_index(container_bg, step_array.len());
let secondary_component =
get_surface_color(base_index, 3, &step_array, is_dark, &control_steps_array[4]);
Container::new(
Component::component(
secondary_component,
accent,
get_text(
color_index(secondary_component, step_array.len()),
&step_array,
&control_steps_array[8],
text_steps_array.as_deref(),
),
Srgba::new(0., 0., 0., 0.0),
Srgba::new(0., 0., 0., 0.0),
is_high_contrast,
control_steps_array[8],
),
container_bg,
get_text(
base_index,
&step_array,
&control_steps_array[8],
text_steps_array.as_deref(),
),
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
is_high_contrast,
)
},
};
theme.spacing = spacing;
theme.corner_radii = corner_radii;
@ -1354,3 +1556,73 @@ impl ThemeBuilder {
Config::new(LIGHT_THEME_BUILDER_ID, Self::VERSION)
}
}
/// Actual blur radius is decided by cosmic-comp,
/// but this represents the strength of the blur effect.
#[allow(missing_docs)]
#[repr(u8)]
#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub enum BlurStrength {
ExtremelyLow,
ExtremelyLow2,
VeryLow,
VeryLow2,
Low,
Low2,
#[default]
Medium,
Medium2,
High,
High2,
VeryHigh,
VeryHigh2,
ExtremelyHigh,
ExtremelyHigh2,
}
impl BlurStrength {
/// Get the alpha value corresponding to the blur strength
/// Lower alpha values correspond to stronger blur effects, and higher alpha values correspond to weaker blur effects. The mapping is as follows:
pub fn alpha(&self) -> f32 {
match self {
Self::ExtremelyLow => 0.90,
Self::ExtremelyLow2 => 0.85,
Self::VeryLow => 0.8,
Self::VeryLow2 => 0.75,
Self::Low => 0.7,
Self::Low2 => 0.65,
Self::Medium => 0.6,
Self::Medium2 => 0.55,
Self::High => 0.5,
Self::High2 => 0.45,
Self::VeryHigh => 0.4,
Self::VeryHigh2 => 0.35,
Self::ExtremelyHigh => 0.25,
Self::ExtremelyHigh2 => 0.2,
}
}
}
impl TryFrom<u8> for BlurStrength {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
0 => Ok(BlurStrength::ExtremelyLow),
1 => Ok(BlurStrength::ExtremelyLow2),
2 => Ok(BlurStrength::VeryLow),
3 => Ok(BlurStrength::VeryLow2),
4 => Ok(BlurStrength::Low),
5 => Ok(BlurStrength::Low2),
6 => Ok(BlurStrength::Medium),
7 => Ok(BlurStrength::Medium2),
8 => Ok(BlurStrength::High),
9 => Ok(BlurStrength::High2),
10 => Ok(BlurStrength::VeryHigh),
11 => Ok(BlurStrength::VeryHigh2),
12 => Ok(BlurStrength::ExtremelyHigh),
13 => Ok(BlurStrength::ExtremelyHigh2),
_ => Err(()),
}
}
}

View file

@ -81,7 +81,7 @@ ForegroundPositive=0,87,44
ForegroundVisited=0,82,90
[Colors:Selection]
BackgroundAlternate=108,149,152
BackgroundAlternate=108,149,153
BackgroundNormal=0,82,90
DecorationFocus=0,82,90
DecorationHover=0,82,90

2
iced

@ -1 +1 @@
Subproject commit 7fd263d99e6ae1b07e51f25bda3367f7463806b1
Subproject commit 0a093b3ab0d5ad1b3ad6b457c1715880276e0ce1

View file

@ -64,6 +64,8 @@ pub enum Action {
Unfocus(iced::window::Id),
/// Windowing system initialized
WindowingSystemInitialized,
/// Blur support enabled
BlurEnabled,
/// Updates the window maximized state
WindowMaximized(iced::window::Id, bool),
/// Updates the tracked window geometry.

View file

@ -6,6 +6,7 @@ use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use super::{Action, Application, ApplicationExt, Subscription};
use crate::core::AppType;
use crate::theme::{THEME, Theme, ThemeType};
use crate::{Core, Element, keyboard_nav};
#[cfg(all(feature = "wayland", target_os = "linux"))]
@ -94,6 +95,7 @@ pub struct Cosmic<App: Application> {
>,
pub tracked_windows: HashSet<window::Id>,
pub opened_surfaces: HashMap<window::Id, u32>,
blur_enabled: bool,
}
impl<T: Application> Cosmic<T>
@ -115,8 +117,8 @@ where
(
Self::new(model),
Task::batch([
command,
iced_runtime::window::run_with_handle(id, init_windowing_system),
command,
]),
)
}
@ -460,6 +462,9 @@ where
)) => {
return Some(Action::WindowState(id, s));
}
wayland::Event::BlurEnabled => {
return Some(Action::BlurEnabled);
}
_ => (),
}
}
@ -673,34 +678,13 @@ impl<T: Application> Cosmic<T> {
state.intersects(WindowState::MAXIMIZED | WindowState::FULLSCREEN);
}
if self.app.core().sync_window_border_radii_to_theme() {
use iced_runtime::platform_specific::wayland::CornerRadius;
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
let theme = THEME.lock().unwrap();
let t = theme.cosmic();
let radii = t.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
let cur_rad = CornerRadius {
top_left: radii[0].round() as u32,
top_right: radii[1].round() as u32,
bottom_right: radii[2].round() as u32,
bottom_left: radii[3].round() as u32,
};
let rounded = !self.app.core().window.sharp_corners;
return Task::batch([corner_radius(
id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard()]);
let cur_rad = self.app.core().app_type.corners(&theme, rounded);
return Task::batch([corner_radius(id, Some(cur_rad)).discard()]);
}
}
@ -773,18 +757,61 @@ impl<T: Application> Cosmic<T> {
if a.distance_squared(*t_inner.accent_color()) > 0.00001 {
theme = Theme::system(Arc::new(t_inner.with_accent(a)));
}
};
}
}
THEME.lock().unwrap().set_theme(theme.theme_type);
let new_blur = self.blur_enabled && {
let t = theme.cosmic();
match self.app.core().app_type() {
crate::core::AppType::Window => t.frosted_windows,
crate::core::AppType::System => t.frosted_system_interface,
crate::core::AppType::Applet => t.frosted_applets,
}
};
theme.transparent = new_blur;
let mut guard = THEME.lock().unwrap();
guard.set_theme(theme.theme_type);
guard.transparent = new_blur;
drop(guard);
let core = self.app.core();
if core.auto_blur {
let mut cmds = Vec::with_capacity(1 + self.tracked_windows.len());
let blur = if new_blur {
iced::window::enable_blur
} else {
iced::window::disable_blur
};
cmds.push(blur(
self.app
.core()
.main_window_id()
.unwrap_or(window::Id::RESERVED),
));
for id in &self.tracked_windows {
cmds.push(blur(*id));
}
return Task::batch(cmds);
}
}
Action::SystemThemeChange(keys, theme) => {
Action::SystemThemeChange(keys, mut theme) => {
let cur_is_dark = THEME.lock().unwrap().theme_type.is_dark();
// Ignore updates if the current theme mode does not match.
if cur_is_dark != theme.cosmic().is_dark {
return iced::Task::none();
}
// update transparent
let new_blur = self.blur_enabled && {
let t = theme.cosmic();
match self.app.core().app_type() {
crate::core::AppType::Window => t.frosted_windows,
crate::core::AppType::System => t.frosted_system_interface,
crate::core::AppType::Applet => t.frosted_applets,
}
};
theme.transparent = new_blur;
let cmd = self.app.system_theme_update(&keys, theme.cosmic());
// Record the last-known system theme in event that the current theme is custom.
self.app.core_mut().system_theme = theme.clone();
@ -808,89 +835,38 @@ impl<T: Application> Cosmic<T> {
} else {
theme
};
new_theme.transparent = new_blur;
new_theme.theme_type.prefer_dark(prefer_dark);
cosmic_theme.set_theme(new_theme.theme_type);
cosmic_theme.transparent = new_blur;
#[cfg(all(feature = "wayland", target_os = "linux"))]
if self.app.core().sync_window_border_radii_to_theme() {
use iced_runtime::platform_specific::wayland::CornerRadius;
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
let t = cosmic_theme.cosmic();
let radii = t.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
let cur_rad = CornerRadius {
top_left: radii[0].round() as u32,
top_right: radii[1].round() as u32,
bottom_right: radii[2].round() as u32,
bottom_left: radii[3].round() as u32,
};
let rounded = !self.app.core().window.sharp_corners;
let cur_rad = self.app.core().app_type.corners(&cosmic_theme, rounded);
// Update radius for the main window
let main_window_id = self
.app
.core()
.main_window_id()
.unwrap_or(window::Id::RESERVED);
let mut cmds = vec![
corner_radius(
main_window_id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard(),
];
let mut cmds =
vec![corner_radius(main_window_id, Some(cur_rad)).discard()];
// Update radius for each tracked view with the window surface type
for (id, (_, surface_type, _)) in self.surface_views.iter() {
if let SurfaceIdWrapper::Window(_) = surface_type {
cmds.push(
corner_radius(
*id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard(),
);
}
let cur_rad = corners(*surface_type, rounded, &cosmic_theme);
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
}
// Update radius for all tracked windows
for id in self.tracked_windows.iter() {
cmds.push(
corner_radius(
*id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard(),
);
for id in &self.tracked_windows {
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
}
return Task::batch(cmds);
@ -921,6 +897,7 @@ impl<T: Application> Cosmic<T> {
} {
return iced::Task::none();
}
let mut cmds = vec![self.app.system_theme_mode_update(&keys, &mode)];
let core = self.app.core_mut();
@ -949,6 +926,15 @@ impl<T: Application> Cosmic<T> {
} else {
new_theme
};
let new_blur = self.blur_enabled && {
let t = new_theme.cosmic();
match core.app_type() {
crate::core::AppType::Window => t.frosted_windows,
crate::core::AppType::System => t.frosted_system_interface,
crate::core::AppType::Applet => t.frosted_applets,
}
};
new_theme.transparent = new_blur;
core.system_theme = new_theme.clone();
{
@ -957,21 +943,14 @@ impl<T: Application> Cosmic<T> {
// Only apply update if the theme is set to load a system theme
if let ThemeType::System { .. } = cosmic_theme.theme_type {
cosmic_theme.set_theme(new_theme.theme_type);
cosmic_theme.transparent = new_blur;
#[cfg(all(feature = "wayland", target_os = "linux"))]
if self.app.core().sync_window_border_radii_to_theme() {
use iced_runtime::platform_specific::wayland::CornerRadius;
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
let t = cosmic_theme.cosmic();
let radii = t.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
let cur_rad = CornerRadius {
top_left: radii[0].round() as u32,
top_right: radii[1].round() as u32,
bottom_right: radii[2].round() as u32,
bottom_left: radii[3].round() as u32,
};
let rounded = !self.app.core().window.sharp_corners;
let cur_rad =
self.app.core().app_type.corners(&cosmic_theme, rounded);
// Update radius for the main window
let main_window_id = self
@ -979,64 +958,35 @@ impl<T: Application> Cosmic<T> {
.core()
.main_window_id()
.unwrap_or(window::Id::RESERVED);
let mut cmds = vec![
corner_radius(
main_window_id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard(),
];
let mut cmds =
vec![corner_radius(main_window_id, Some(cur_rad)).discard()];
// Update radius for each tracked view with the window surface type
for (id, (_, surface_type, _)) in self.surface_views.iter() {
if let SurfaceIdWrapper::Window(_) = surface_type {
cmds.push(
corner_radius(
*id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard(),
);
}
let cur_rad = corners(*surface_type, rounded, &cosmic_theme);
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
}
// Update radius for all tracked windows
for id in self.tracked_windows.iter() {
cmds.push(
corner_radius(
*id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard(),
);
for id in &self.tracked_windows {
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
}
let core = self.app.core();
if core.auto_blur {
let blur = if cosmic_theme.transparent {
iced::window::enable_blur
} else {
iced::window::disable_blur
};
cmds.push(blur(
self.app
.core()
.main_window_id()
.unwrap_or(window::Id::RESERVED),
));
for id in &self.tracked_windows {
cmds.push(blur(*id));
}
}
return Task::batch(cmds);
@ -1059,7 +1009,7 @@ impl<T: Application> Cosmic<T> {
#[allow(clippy::used_underscore_binding)]
_token,
),
)
);
}
#[cfg(not(all(feature = "wayland", target_os = "linux")))]
@ -1136,18 +1086,48 @@ impl<T: Application> Cosmic<T> {
if changed {
core.theme_sub_counter += 1;
let new_theme = if is_dark {
let mut new_theme = if is_dark {
crate::theme::system_dark()
} else {
crate::theme::system_light()
};
if let ThemeType::System { .. } = new_theme.theme_type {
let new_blur = self.blur_enabled && {
let t = new_theme.cosmic();
match core.app_type() {
crate::core::AppType::Window => t.frosted_windows,
crate::core::AppType::System => t.frosted_system_interface,
crate::core::AppType::Applet => t.frosted_applets,
}
};
new_theme.transparent = new_blur;
}
core.system_theme = new_theme.clone();
{
let mut cosmic_theme = THEME.lock().unwrap();
// Only apply update if the theme is set to load a system theme
if let ThemeType::System { theme: _, .. } = cosmic_theme.theme_type {
let mut cmds = Vec::with_capacity(1 + self.tracked_windows.len());
if core.auto_blur {
let blur = if cosmic_theme.transparent {
iced::window::enable_blur
} else {
iced::window::disable_blur
};
cmds.push(blur(
self.app
.core()
.main_window_id()
.unwrap_or(window::Id::RESERVED),
));
for id in &self.tracked_windows {
cmds.push(blur(*id));
}
}
cosmic_theme.set_theme(new_theme.theme_type);
return Task::batch(cmds);
}
}
}
@ -1249,43 +1229,91 @@ impl<T: Application> Cosmic<T> {
Action::Opened(id) => {
#[cfg(all(feature = "wayland", target_os = "linux"))]
if self.app.core().sync_window_border_radii_to_theme() {
use iced_runtime::platform_specific::wayland::CornerRadius;
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
let theme = THEME.lock().unwrap();
let t = theme.cosmic();
let radii = t.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
let cur_rad = CornerRadius {
top_left: radii[0].round() as u32,
top_right: radii[1].round() as u32,
bottom_right: radii[2].round() as u32,
bottom_left: radii[3].round() as u32,
};
let mut theme = THEME.lock().unwrap();
// TODO do we need per window sharp corners?
let rounded = !self.app.core().window.sharp_corners;
let core = self.app.core();
let new_blur = self.blur_enabled && {
let t = theme.cosmic();
match self.app.core().app_type() {
crate::core::AppType::Window => t.frosted_windows,
crate::core::AppType::System => t.frosted_system_interface,
crate::core::AppType::Applet => t.frosted_applets,
}
};
theme.transparent = new_blur;
let blur_cmd = if core.auto_blur {
let blur = if new_blur {
iced::window::enable_blur
} else {
iced::window::disable_blur
};
let mut cmds = Vec::with_capacity(1 + self.tracked_windows.len());
cmds.push(blur(id));
Task::batch(cmds)
} else {
Task::none()
};
let corner_task = if let Some(s) = self.surface_views.get(&id) {
corner_radius(id, Some(corners(s.1, rounded, &theme))).discard()
} else if id
== self
.app
.core()
.main_window_id()
.unwrap_or(window::Id::RESERVED)
|| self.tracked_windows.contains(&id)
{
corner_radius(id, Some(self.app.core().app_type.corners(&theme, rounded)))
.discard()
} else {
Task::none()
};
return Task::batch([
corner_radius(
id,
if rounded {
Some(cur_rad)
} else {
let rad_0 = t.radius_0();
Some(CornerRadius {
top_left: rad_0[0].round() as u32,
top_right: rad_0[1].round() as u32,
bottom_right: rad_0[2].round() as u32,
bottom_left: rad_0[3].round() as u32,
})
},
)
.discard(),
blur_cmd,
corner_task,
iced_runtime::window::run_with_handle(id, init_windowing_system),
]);
}
return iced_runtime::window::run_with_handle(id, init_windowing_system);
}
_ => {}
Action::BlurEnabled => {
// TODO do this after blur event confirms support instead of for all wayland windows
let core = self.app.core();
self.blur_enabled = true;
let new_blur = self.blur_enabled && {
let t = core.system_theme.cosmic();
match self.app.core().app_type() {
crate::core::AppType::Window => t.frosted_windows,
crate::core::AppType::System => t.frosted_system_interface,
crate::core::AppType::Applet => t.frosted_applets,
}
};
let mut t = THEME.lock().unwrap();
t.transparent = matches!(&t.theme_type, ThemeType::System { .. }) && new_blur;
if core.auto_blur {
let blur = if new_blur {
iced::window::enable_blur
} else {
iced::window::disable_blur
};
let mut cmds = Vec::with_capacity(1 + self.tracked_windows.len());
if let Some(main_id) = core.main_window_id() {
cmds.push(blur(main_id));
}
for id in &self.tracked_windows {
cmds.push(blur(*id));
}
return Task::batch(cmds);
}
}
_ => (),
}
iced::Task::none()
@ -1300,6 +1328,7 @@ impl<App: Application> Cosmic<App> {
surface_views: HashMap::new(),
tracked_windows: HashSet::new(),
opened_surfaces: HashMap::new(),
blur_enabled: false,
}
}
@ -1374,3 +1403,39 @@ impl<App: Application> Cosmic<App> {
.discard()
}
}
#[cfg(all(feature = "wayland", target_os = "linux"))]
fn corners(
surface_type: SurfaceIdWrapper,
rounded: bool,
theme: &Theme,
) -> iced_runtime::platform_specific::wayland::CornerRadius {
let theme = theme.cosmic();
if let SurfaceIdWrapper::Popup(_) = surface_type {
let radius_m = theme.radius_m();
iced_runtime::platform_specific::wayland::CornerRadius {
top_left: radius_m[0].round() as u32,
top_right: radius_m[1].round() as u32,
bottom_right: radius_m[2].round() as u32,
bottom_left: radius_m[3].round() as u32,
}
} else if let SurfaceIdWrapper::Window(_) = surface_type
&& rounded
{
let radius_0 = theme.radius_0();
iced_runtime::platform_specific::wayland::CornerRadius {
top_left: radius_0[0].round() as u32,
top_right: radius_0[1].round() as u32,
bottom_right: radius_0[2].round() as u32,
bottom_left: radius_0[3].round() as u32,
}
} else {
let radius_s = theme.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
iced_runtime::platform_specific::wayland::CornerRadius {
top_left: radius_s[0].round() as u32,
top_right: radius_s[1].round() as u32,
bottom_right: radius_s[2].round() as u32,
bottom_left: radius_s[3].round() as u32,
}
}
}

View file

@ -820,7 +820,7 @@ impl<App: Application> ApplicationExt for App {
let cosmic = theme.cosmic();
container::Style {
background: Some(iced::Background::Color(
cosmic.background.base.into(),
cosmic.background(theme.transparent).base.into(),
)),
border: iced::Border {
radius: [
@ -849,7 +849,7 @@ impl<App: Application> ApplicationExt for App {
container::Style {
background: if content_container {
Some(iced::Background::Color(
theme.cosmic().background.base.into(),
theme.cosmic().background(theme.transparent).base.into(),
))
} else {
None

View file

@ -227,7 +227,7 @@ impl Context {
let icon = widget::icon(icon)
.class(if symbolic {
theme::Svg::Custom(Rc::new(|theme| iced_widget::svg::Style {
color: Some(theme.cosmic().background.on.into()),
color: Some(theme.cosmic().background(theme.transparent).on.into()),
}))
} else {
theme::Svg::default()
@ -378,16 +378,18 @@ impl Context {
Container::<Message, _, Renderer>::new(content).style(|theme| {
let cosmic = theme.cosmic();
let corners = cosmic.corner_radii;
let mut bg = cosmic.background(theme.transparent).base;
bg.alpha = (bg.alpha + if cosmic.is_dark { 0.6 } else { 0.5 }).min(1.);
iced_widget::container::Style {
text_color: Some(cosmic.background.on.into()),
background: Some(Color::from(cosmic.background.base).into()),
text_color: Some(cosmic.background(theme.transparent).on.into()),
background: Some(Color::from(bg).into()),
border: iced::Border {
radius: corners.radius_m.into(),
width: 1.0,
color: cosmic.background.divider.into(),
color: cosmic.background(theme.transparent).divider.into(),
},
shadow: Shadow::default(),
icon_color: Some(cosmic.background.on.into()),
icon_color: Some(cosmic.background(theme.transparent).on.into()),
snap: true,
}
}),
@ -565,6 +567,7 @@ pub fn run<App: Application>(flags: App::Flags) -> iced::Result {
core.window.show_maximize = false;
core.window.show_minimize = false;
core.window.use_template = false;
core.app_type = crate::core::AppType::Applet;
window_settings.decorations = false;
window_settings.exit_on_close_request = true;

View file

@ -65,7 +65,6 @@ pub fn file_transfer_send(
/// Returns a list of file paths.
#[cfg(feature = "xdg-portal")]
pub fn file_transfer_receive(key: String) -> iced::Task<ashpd::Result<Vec<String>>> {
dbg!(&key);
iced::Task::future(async move {
let file_transfer = ashpd::documents::FileTransfer::new().await?;
file_transfer.retrieve_files(&key).await

View file

@ -101,6 +101,59 @@ pub struct Core {
#[cfg(all(feature = "wayland", target_os = "linux"))]
pub(crate) sync_window_border_radii_to_theme: bool,
pub(crate) auto_blur: bool,
pub(crate) app_type: AppType,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum AppType {
/// A regular application
Window,
/// A system application
System,
/// An applet
Applet,
}
impl AppType {
/// Calculate suggested corners for each app type main window
#[cfg(all(feature = "wayland", target_os = "linux"))]
pub fn corners(
&self,
theme: &Theme,
rounded: bool,
) -> iced_runtime::platform_specific::wayland::CornerRadius {
let theme = theme.cosmic();
if let Self::Applet = self {
let radius_l = theme.radius_l();
iced_runtime::platform_specific::wayland::CornerRadius {
top_left: radius_l[0].round() as u32,
top_right: radius_l[1].round() as u32,
bottom_right: radius_l[2].round() as u32,
bottom_left: radius_l[3].round() as u32,
}
} else if let Self::Window = self
&& rounded
{
let radius_0 = theme.radius_0();
iced_runtime::platform_specific::wayland::CornerRadius {
top_left: radius_0[0].round() as u32,
top_right: radius_0[1].round() as u32,
bottom_right: radius_0[2].round() as u32,
bottom_left: radius_0[3].round() as u32,
}
} else {
let radius_s = theme.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
iced_runtime::platform_specific::wayland::CornerRadius {
top_left: radius_s[0].round() as u32,
top_right: radius_s[1].round() as u32,
bottom_right: radius_s[2].round() as u32,
bottom_left: radius_s[3].round() as u32,
}
}
}
}
impl Default for Core {
@ -161,6 +214,8 @@ impl Default for Core {
menu_bars: HashMap::new(),
#[cfg(all(feature = "wayland", target_os = "linux"))]
sync_window_border_radii_to_theme: true,
auto_blur: true,
app_type: AppType::Window,
}
}
}
@ -502,4 +557,20 @@ impl Core {
pub fn sync_window_border_radii_to_theme(&self) -> bool {
self.sync_window_border_radii_to_theme
}
pub fn set_auto_blur(&mut self, auto_blur: bool) {
self.auto_blur = auto_blur;
}
pub fn auto_blur(&self) -> bool {
self.auto_blur
}
pub fn set_app_type(&mut self, app_type: AppType) {
self.app_type = app_type;
}
pub fn app_type(&self) -> AppType {
self.app_type
}
}

View file

@ -50,6 +50,7 @@ pub static TRANSPARENT_COMPONENT: LazyLock<Component> = LazyLock::new(|| Compone
pub(crate) static THEME: Mutex<Theme> = Mutex::new(Theme {
theme_type: ThemeType::Dark,
layer: cosmic_theme::Layer::Background,
transparent: false,
});
/// Currently-defined theme.
@ -213,6 +214,7 @@ impl ThemeType {
pub struct Theme {
pub theme_type: ThemeType,
pub layer: cosmic_theme::Layer,
pub transparent: bool,
}
impl Theme {
@ -283,9 +285,9 @@ impl Theme {
/// can be used in a component that is intended to be a child of a `CosmicContainer`
pub fn current_container(&self) -> &cosmic_theme::Container {
match self.layer {
cosmic_theme::Layer::Background => &self.cosmic().background,
cosmic_theme::Layer::Primary => &self.cosmic().primary,
cosmic_theme::Layer::Secondary => &self.cosmic().secondary,
cosmic_theme::Layer::Background => &self.cosmic().background(self.transparent),
cosmic_theme::Layer::Primary => &self.cosmic().primary(self.transparent),
cosmic_theme::Layer::Secondary => &self.cosmic().secondary(self.transparent),
}
}

View file

@ -128,20 +128,20 @@ pub fn appearance(
let (background, _, _) = color(&cosmic.text_button);
appearance.background = Some(Background::Color(background));
appearance.icon_color = Some(cosmic.background.on.into());
appearance.text_color = Some(cosmic.background.on.into());
appearance.icon_color = Some(cosmic.background(theme.transparent).on.into());
appearance.text_color = Some(cosmic.background(theme.transparent).on.into());
corner_radii = &cosmic.corner_radii.radius_0;
}
Button::AppletIcon => {
let (background, _, _) = color(&cosmic.text_button);
appearance.background = Some(Background::Color(background));
appearance.icon_color = Some(cosmic.background.on.into());
appearance.text_color = Some(cosmic.background.on.into());
appearance.icon_color = Some(cosmic.background(theme.transparent).on.into());
appearance.text_color = Some(cosmic.background(theme.transparent).on.into());
}
Button::MenuFolder => {
// Menu folders cannot be disabled, ignore customized icon and text color
let component = &cosmic.background.component;
let component = &cosmic.background(theme.transparent).component;
let (background, _, _) = color(component);
appearance.background = Some(Background::Color(background));
appearance.icon_color = Some(component.on.into());
@ -150,11 +150,12 @@ pub fn appearance(
}
Button::ListItem => {
corner_radii = &[0.0; 4];
let (background, text, icon) = color(&cosmic.background.component);
let (background, text, icon) = color(&cosmic.background(theme.transparent).component);
if selected {
appearance.background =
Some(Background::Color(cosmic.primary.component.hover.into()));
appearance.background = Some(Background::Color(
cosmic.primary(theme.transparent).component.hover.into(),
));
appearance.icon_color = Some(cosmic.accent.base.into());
appearance.text_color = Some(cosmic.accent_text_color().into());
} else {
@ -164,7 +165,7 @@ pub fn appearance(
}
}
Button::MenuItem => {
let (background, text, icon) = color(&cosmic.background.component);
let (background, text, icon) = color(&cosmic.background(theme.transparent).component);
appearance.background = Some(Background::Color(background));
appearance.icon_color = icon;
appearance.text_color = text;
@ -280,6 +281,6 @@ impl Catalog for crate::Theme {
}
fn selection_background(&self) -> Background {
Background::Color(self.cosmic().primary.base.into())
Background::Color(self.cosmic().primary(self.transparent).base.into())
}
}

View file

@ -13,18 +13,28 @@ impl dropdown::menu::StyleSheet for Theme {
dropdown::menu::Appearance {
text_color: cosmic.on_bg_color().into(),
background: Background::Color(cosmic.background.component.base.into()),
background: Background::Color(
cosmic.background(self.transparent).component.base.into(),
),
border_width: 0.0,
border_radius: cosmic.corner_radii.radius_m.into(),
border_color: Color::TRANSPARENT,
hovered_text_color: cosmic.on_bg_color().into(),
hovered_background: Background::Color(cosmic.primary.component.hover.into()),
hovered_background: Background::Color(
cosmic.primary(self.transparent).component.hover.into(),
),
selected_text_color: cosmic.accent_text_color().into(),
selected_background: Background::Color(cosmic.primary.component.hover.into()),
selected_background: Background::Color(
cosmic.primary(self.transparent).component.hover.into(),
),
description_color: cosmic.primary.component.on_disabled.into(),
description_color: cosmic
.primary(self.transparent)
.component
.on_disabled
.into(),
}
}
}

View file

@ -228,11 +228,11 @@ impl iced_checkbox::Catalog for Theme {
},
Checkbox::Secondary => iced_checkbox::Style {
background: Background::Color(if is_checked {
cosmic.background.component.base.into()
cosmic.background(self.transparent).component.base.into()
} else {
self.current_container().small_widget.into()
}),
icon_color: cosmic.background.on.into(),
icon_color: cosmic.background(self.transparent).on.into(),
border: Border {
radius: corners.radius_xs.into(),
width: if is_checked { 0.0 } else { 1.0 },
@ -413,11 +413,13 @@ impl<'a> Container<'a> {
}
#[must_use]
pub fn background(theme: &cosmic_theme::Theme) -> iced_container::Style {
pub fn background(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
iced_container::Style {
icon_color: Some(Color::from(theme.background.on)),
text_color: Some(Color::from(theme.background.on)),
background: Some(iced::Background::Color(theme.background.base.into())),
icon_color: Some(Color::from(theme.background(transparent).on)),
text_color: Some(Color::from(theme.background(transparent).on)),
background: Some(iced::Background::Color(
theme.background(transparent).base.into(),
)),
border: Border {
radius: theme.corner_radii.radius_s.into(),
..Default::default()
@ -428,11 +430,13 @@ impl<'a> Container<'a> {
}
#[must_use]
pub fn primary(theme: &cosmic_theme::Theme) -> iced_container::Style {
pub fn primary(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
iced_container::Style {
icon_color: Some(Color::from(theme.primary.on)),
text_color: Some(Color::from(theme.primary.on)),
background: Some(iced::Background::Color(theme.primary.base.into())),
icon_color: Some(Color::from(theme.primary(transparent).on)),
text_color: Some(Color::from(theme.primary(transparent).on)),
background: Some(iced::Background::Color(
theme.primary(transparent).base.into(),
)),
border: Border {
radius: theme.corner_radii.radius_s.into(),
..Default::default()
@ -443,11 +447,13 @@ impl<'a> Container<'a> {
}
#[must_use]
pub fn secondary(theme: &cosmic_theme::Theme) -> iced_container::Style {
pub fn secondary(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
iced_container::Style {
icon_color: Some(Color::from(theme.secondary.on)),
text_color: Some(Color::from(theme.secondary.on)),
background: Some(iced::Background::Color(theme.secondary.base.into())),
icon_color: Some(Color::from(theme.secondary(transparent).on)),
text_color: Some(Color::from(theme.secondary(transparent).on)),
background: Some(iced::Background::Color(
theme.secondary(transparent).base.into(),
)),
border: Border {
radius: theme.corner_radii.radius_s.into(),
..Default::default()
@ -478,14 +484,30 @@ impl iced_container::Catalog for Theme {
let window_corner_radius = cosmic.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
match class {
Container::Transparent => iced_container::Style::default(),
Container::Transparent => {
let component = &self.current_container().component;
iced_container::Style {
icon_color: Some(component.on.into()),
text_color: Some(component.on.into()),
background: None,
border: Border {
radius: 0.into(),
..Default::default()
},
shadow: Shadow::default(),
snap: true,
}
}
Container::Custom(f) => f(self),
Container::WindowBackground => iced_container::Style {
icon_color: Some(Color::from(cosmic.background.on)),
text_color: Some(Color::from(cosmic.background.on)),
background: Some(iced::Background::Color(cosmic.background.base.into())),
icon_color: Some(Color::from(cosmic.background(self.transparent).on)),
text_color: Some(Color::from(cosmic.background(self.transparent).on)),
background: Some(iced::Background::Color(
cosmic.background(self.transparent).base.into(),
)),
border: Border {
radius: [
cosmic.corner_radii.radius_0[0],
@ -523,12 +545,13 @@ impl iced_container::Catalog for Theme {
let (icon_color, text_color) = if *focused {
(
Color::from(cosmic.accent_text_color()),
Color::from(cosmic.background.on),
Color::from(cosmic.background(self.transparent).on),
)
} else {
use crate::ext::ColorExt;
let unfocused_color = Color::from(cosmic.background.component.on)
.blend_alpha(cosmic.background.base.into(), 0.5);
let unfocused_color =
Color::from(cosmic.background(self.transparent).component.on)
.blend_alpha(cosmic.background(self.transparent).base.into(), 0.5);
(unfocused_color, unfocused_color)
};
@ -538,7 +561,9 @@ impl iced_container::Catalog for Theme {
background: if *transparent {
None
} else {
Some(iced::Background::Color(cosmic.background.base.into()))
Some(iced::Background::Color(
cosmic.background(self.transparent).base.into(),
))
},
border: Border {
radius: [
@ -564,20 +589,23 @@ impl iced_container::Catalog for Theme {
}
Container::ContextDrawer => {
let mut a = Container::primary(cosmic);
let mut a = Container::primary(cosmic, self.transparent);
if let Some(Background::Color(ref mut color)) = a.background {
color.a = (color.a + if cosmic.is_dark { 0.60 } else { 0.5 }).min(1.);
}
if cosmic.is_high_contrast {
a.border.width = 1.;
a.border.color = cosmic.primary.divider.into();
a.border.color = cosmic.primary(self.transparent).divider.into();
}
a
}
Container::Background => Container::background(cosmic),
Container::Background => Container::background(cosmic, self.transparent),
Container::Primary => Container::primary(cosmic),
Container::Primary => Container::primary(cosmic, self.transparent),
Container::Secondary => Container::secondary(cosmic),
Container::Secondary => Container::secondary(cosmic, self.transparent),
Container::Dropdown => iced_container::Style {
icon_color: None,
@ -609,10 +637,14 @@ impl iced_container::Catalog for Theme {
match self.layer {
cosmic_theme::Layer::Background => iced_container::Style {
icon_color: Some(Color::from(cosmic.background.component.on)),
text_color: Some(Color::from(cosmic.background.component.on)),
icon_color: Some(Color::from(
cosmic.background(self.transparent).component.on,
)),
text_color: Some(Color::from(
cosmic.background(self.transparent).component.on,
)),
background: Some(iced::Background::Color(
cosmic.background.component.base.into(),
cosmic.background(self.transparent).component.base.into(),
)),
border: Border {
radius: cosmic.corner_radii.radius_s.into(),
@ -622,10 +654,14 @@ impl iced_container::Catalog for Theme {
snap: true,
},
cosmic_theme::Layer::Primary => iced_container::Style {
icon_color: Some(Color::from(cosmic.primary.component.on)),
text_color: Some(Color::from(cosmic.primary.component.on)),
icon_color: Some(Color::from(
cosmic.primary(self.transparent).component.on,
)),
text_color: Some(Color::from(
cosmic.primary(self.transparent).component.on,
)),
background: Some(iced::Background::Color(
cosmic.primary.component.base.into(),
cosmic.primary(self.transparent).component.base.into(),
)),
border: Border {
radius: cosmic.corner_radii.radius_s.into(),
@ -635,10 +671,14 @@ impl iced_container::Catalog for Theme {
snap: true,
},
cosmic_theme::Layer::Secondary => iced_container::Style {
icon_color: Some(Color::from(cosmic.secondary.component.on)),
text_color: Some(Color::from(cosmic.secondary.component.on)),
icon_color: Some(Color::from(
cosmic.secondary(self.transparent).component.on,
)),
text_color: Some(Color::from(
cosmic.secondary(self.transparent).component.on,
)),
background: Some(iced::Background::Color(
cosmic.secondary.component.base.into(),
cosmic.secondary(self.transparent).component.base.into(),
)),
border: Border {
radius: cosmic.corner_radii.radius_s.into(),
@ -651,11 +691,13 @@ impl iced_container::Catalog for Theme {
}
Container::Dialog => iced_container::Style {
icon_color: Some(Color::from(cosmic.primary.on)),
text_color: Some(Color::from(cosmic.primary.on)),
background: Some(iced::Background::Color(cosmic.primary.base.into())),
icon_color: Some(Color::from(cosmic.primary(self.transparent).on)),
text_color: Some(Color::from(cosmic.primary(self.transparent).on)),
background: Some(iced::Background::Color(
cosmic.primary(self.transparent).base.into(),
)),
border: Border {
color: cosmic.primary.divider.into(),
color: cosmic.primary(self.transparent).divider.into(),
width: 1.0,
radius: cosmic.corner_radii.radius_m.into(),
},
@ -797,13 +839,15 @@ impl menu::Catalog for Theme {
menu::Style {
text_color: cosmic.on_bg_color().into(),
background: Background::Color(cosmic.background.base.into()),
background: Background::Color(cosmic.background(self.transparent).base.into()),
border: Border {
radius: cosmic.corner_radii.radius_m.into(),
..Default::default()
},
selected_text_color: cosmic.accent_text_color().into(),
selected_background: Background::Color(cosmic.background.component.hover.into()),
selected_background: Background::Color(
cosmic.background(self.transparent).component.hover.into(),
),
shadow: Default::default(),
}
}
@ -841,7 +885,7 @@ impl pick_list::Catalog for Theme {
match status {
pick_list::Status::Active => appearance,
pick_list::Status::Hovered => pick_list::Style {
background: Background::Color(cosmic.background.base.into()),
background: Background::Color(cosmic.background(self.transparent).base.into()),
..appearance
},
pick_list::Status::Opened { is_hovered: _ } => appearance,
@ -1037,7 +1081,10 @@ impl progress_bar::Catalog for Theme {
},
)
} else {
(theme.accent.base, theme.background.divider)
(
theme.accent.base,
theme.background(self.transparent).divider,
)
};
let border = Border {
radius: theme.corner_radii.radius_xl.into(),
@ -1510,7 +1557,7 @@ impl iced_widget::text_editor::Catalog for Theme {
let selection = cosmic.accent.base.into();
let value = cosmic.palette.neutral_9.into();
let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into();
let icon: Color = cosmic.background.on.into();
let icon: Color = cosmic.background(self.transparent).on.into();
// TODO do we need to add icon color back?
match status {

View file

@ -64,11 +64,13 @@ impl StyleSheet for Theme {
fn appearance(&self, style: &Self::Style) -> Appearance {
let cosmic = self.cosmic();
let component = &cosmic.background.component;
let component = &cosmic.background(self.transparent).component;
let mut bg = component.base;
bg.alpha = (bg.alpha + if cosmic.is_dark { 0.6 } else { 0.5 }).min(1.);
match style {
MenuBarStyle::Default => Appearance {
background: component.base.into(),
background: bg.into(),
border_width: 1.0,
bar_border_radius: cosmic.corner_radii.radius_xl,
menu_border_radius: cosmic.corner_radii.radius_s.map(|x| x + 2.0),

View file

@ -31,16 +31,26 @@ impl crate::widget::card::style::Catalog for crate::Theme {
match self.layer {
cosmic_theme::Layer::Background => crate::widget::card::style::Style {
card_1: Background::Color(cosmic.background.component.hover.into()),
card_2: Background::Color(cosmic.background.component.pressed.into()),
card_1: Background::Color(
cosmic.background(self.transparent).component.hover.into(),
),
card_2: Background::Color(
cosmic.background(self.transparent).component.pressed.into(),
),
},
cosmic_theme::Layer::Primary => crate::widget::card::style::Style {
card_1: Background::Color(cosmic.primary.component.hover.into()),
card_2: Background::Color(cosmic.primary.component.pressed.into()),
card_1: Background::Color(cosmic.primary(self.transparent).component.hover.into()),
card_2: Background::Color(
cosmic.primary(self.transparent).component.pressed.into(),
),
},
cosmic_theme::Layer::Secondary => crate::widget::card::style::Style {
card_1: Background::Color(cosmic.secondary.component.hover.into()),
card_2: Background::Color(cosmic.secondary.component.pressed.into()),
card_1: Background::Color(
cosmic.secondary(self.transparent).component.hover.into(),
),
card_2: Background::Color(
cosmic.secondary(self.transparent).component.pressed.into(),
),
},
}
}

View file

@ -230,7 +230,7 @@ pub fn menu_items<
}
fn key_style(theme: &crate::Theme) -> TextStyle {
let mut color = theme.cosmic().background.component.on;
let mut color = theme.cosmic().background(theme.transparent).component.on;
color.alpha *= 0.75;
TextStyle {
color: Some(color.into()),

View file

@ -173,7 +173,9 @@ pub fn nav_bar_style(theme: &Theme) -> iced_widget::container::Style {
iced_widget::container::Style {
icon_color: Some(cosmic.on_bg_color().into()),
text_color: Some(cosmic.on_bg_color().into()),
background: Some(Background::Color(cosmic.primary.base.into())),
background: Some(Background::Color(
cosmic.primary(theme.transparent).base.into(),
)),
border: Border {
width: 0.0,
color: Color::TRANSPARENT,