Compare commits
17 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95756b1a57 | ||
|
|
c423ad1bfc | ||
|
|
8d7bcab258 | ||
|
|
c162a1f24a | ||
|
|
3f9e93067b | ||
|
|
917af9fda2 | ||
|
|
9b465a8b5c | ||
|
|
9cac422c24 | ||
|
|
0fc4638af3 | ||
|
|
3d8d8915be | ||
|
|
46d9f0c344 | ||
|
|
0d69cd9183 | ||
|
|
52116d2f36 | ||
|
|
0e72508dcc | ||
|
|
1d7113a244 | ||
|
|
e287a789c1 | ||
|
|
6caccaba33 |
47 changed files with 969 additions and 1492 deletions
12
Cargo.toml
12
Cargo.toml
|
|
@ -58,6 +58,7 @@ desktop = [
|
||||||
"process",
|
"process",
|
||||||
"dep:cosmic-settings-config",
|
"dep:cosmic-settings-config",
|
||||||
"dep:freedesktop-desktop-entry",
|
"dep:freedesktop-desktop-entry",
|
||||||
|
"dep:image-extras",
|
||||||
"dep:mime",
|
"dep:mime",
|
||||||
"dep:shlex",
|
"dep:shlex",
|
||||||
"tokio?/io-util",
|
"tokio?/io-util",
|
||||||
|
|
@ -126,7 +127,7 @@ ashpd = { version = "0.12.3", default-features = false, optional = true }
|
||||||
async-fs = { version = "2.2", optional = true }
|
async-fs = { version = "2.2", optional = true }
|
||||||
async-std = { version = "1.13", optional = true }
|
async-std = { version = "1.13", optional = true }
|
||||||
auto_enums = "0.8.8"
|
auto_enums = "0.8.8"
|
||||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "a7d2d7a", optional = true }
|
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086", optional = true }
|
||||||
jiff = "0.2"
|
jiff = "0.2"
|
||||||
cosmic-config = { path = "cosmic-config" }
|
cosmic-config = { path = "cosmic-config" }
|
||||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
|
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
|
||||||
|
|
@ -141,9 +142,14 @@ css-color = "0.2.8"
|
||||||
derive_setters = "0.1.9"
|
derive_setters = "0.1.9"
|
||||||
futures = "0.3"
|
futures = "0.3"
|
||||||
image = { version = "0.25.10", default-features = false, features = [
|
image = { version = "0.25.10", default-features = false, features = [
|
||||||
|
"ico",
|
||||||
"jpeg",
|
"jpeg",
|
||||||
"png",
|
"png",
|
||||||
] }
|
] }
|
||||||
|
image-extras = { version = "0.1.0", default-features = false, features = [
|
||||||
|
"xpm",
|
||||||
|
"xbm",
|
||||||
|
], optional = true }
|
||||||
libc = { version = "0.2.183", optional = true }
|
libc = { version = "0.2.183", optional = true }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
mime = { version = "0.3.17", optional = true }
|
mime = { version = "0.3.17", optional = true }
|
||||||
|
|
@ -170,12 +176,12 @@ cosmic-config = { path = "cosmic-config", features = ["dbus"] }
|
||||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||||
zbus = { version = "5.14.0", default-features = false }
|
zbus = { version = "5.14.0", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
||||||
freedesktop-icons = { package = "cosmic-freedesktop-icons", git = "https://github.com/pop-os/freedesktop-icons" }
|
freedesktop-icons = { package = "cosmic-freedesktop-icons", git = "https://github.com/pop-os/freedesktop-icons" }
|
||||||
freedesktop-desktop-entry = { version = "0.8.1", optional = true }
|
freedesktop-desktop-entry = { version = "0.8.1", optional = true }
|
||||||
shlex = { version = "1.3.0", optional = true }
|
shlex = { version = "1.3.0", optional = true }
|
||||||
|
|
||||||
[target.'cfg(not(unix))'.dependencies]
|
[target.'cfg(any(not(unix), target_os = "macos"))'.dependencies]
|
||||||
# Used to embed bundled icons for non-unix platforms.
|
# Used to embed bundled icons for non-unix platforms.
|
||||||
phf = { version = "0.13.1", features = ["macros"] }
|
phf = { version = "0.13.1", features = ["macros"] }
|
||||||
|
|
||||||
|
|
|
||||||
4
build.rs
4
build.rs
|
|
@ -3,7 +3,9 @@ use std::env;
|
||||||
fn main() {
|
fn main() {
|
||||||
println!("cargo::rerun-if-changed=build.rs");
|
println!("cargo::rerun-if-changed=build.rs");
|
||||||
|
|
||||||
if env::var_os("CARGO_CFG_UNIX").is_none() {
|
if env::var_os("CARGO_CFG_UNIX").is_none()
|
||||||
|
|| env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos")
|
||||||
|
{
|
||||||
generate_bundled_icons();
|
generate_bundled_icons();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn;
|
use syn::{self};
|
||||||
|
|
||||||
#[proc_macro_derive(CosmicConfigEntry, attributes(version, id, cosmic_config_entry))]
|
#[proc_macro_derive(CosmicConfigEntry, attributes(version, id))]
|
||||||
pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream {
|
pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream {
|
||||||
// Construct a representation of Rust code as a syntax tree
|
// Construct a representation of Rust code as a syntax tree
|
||||||
// that we can manipulate
|
// that we can manipulate
|
||||||
|
|
@ -12,25 +12,6 @@ pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream {
|
||||||
impl_cosmic_config_entry_macro(&ast)
|
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 {
|
fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let attributes = &ast.attrs;
|
let attributes = &ast.attrs;
|
||||||
let version = attributes
|
let version = attributes
|
||||||
|
|
@ -67,54 +48,19 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
|
|
||||||
let write_each_config_field = fields.iter().map(|field| {
|
let write_each_config_field = fields.iter().map(|field| {
|
||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
let with = match get_cosmic_config_attrs(field) {
|
quote! {
|
||||||
Ok(attrs) => attrs,
|
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
|
||||||
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 get_each_config_field = fields.iter().map(|field| {
|
||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
let field_type = &field.ty;
|
let field_type = &field.ty;
|
||||||
let with = match get_cosmic_config_attrs(field) {
|
quote! {
|
||||||
Ok(attrs) => attrs,
|
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||||
Err(e) => {
|
Ok(#field_name) => default.#field_name = #field_name,
|
||||||
return e.to_compile_error();
|
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
|
||||||
}
|
Err(e) => errors.push(e),
|
||||||
};
|
|
||||||
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -122,39 +68,17 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let update_each_config_field = fields.iter().map(|field| {
|
let update_each_config_field = fields.iter().map(|field| {
|
||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
let field_type = &field.ty;
|
let field_type = &field.ty;
|
||||||
let with = match get_cosmic_config_attrs(field) {
|
quote! {
|
||||||
Ok(attrs) => attrs,
|
stringify!(#field_name) => {
|
||||||
Err(e) => {
|
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||||
return e.to_compile_error();
|
Ok(value) => {
|
||||||
}
|
if self.#field_name != value {
|
||||||
};
|
keys.push(stringify!(#field_name));
|
||||||
|
}
|
||||||
if let Some(with) = with {
|
self.#field_name = value;
|
||||||
quote! {
|
},
|
||||||
stringify!(#field_name) => {
|
Err(e) => {
|
||||||
match cosmic_config::ConfigGet::get::<#with>(config, stringify!(#field_name)) {
|
errors.push(e);
|
||||||
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),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -162,7 +162,6 @@ pub trait ConfigSet {
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
system_path: Option<PathBuf>,
|
system_path: Option<PathBuf>,
|
||||||
user_path: Option<PathBuf>,
|
user_path: Option<PathBuf>,
|
||||||
previous: Option<Box<Config>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that the name is relative and doesn't contain . or ..
|
/// Check that the name is relative and doesn't contain . or ..
|
||||||
|
|
@ -181,13 +180,9 @@ fn sanitize_name(name: &str) -> Result<&Path, Error> {
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Get a system config for the given name and config version
|
/// Get a system config for the given name and config version
|
||||||
pub fn system(name: &str, version: u64) -> Result<Self, Error> {
|
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}"));
|
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||||
#[cfg(unix)]
|
#[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)]
|
#[cfg(windows)]
|
||||||
let system_path =
|
let system_path =
|
||||||
|
|
@ -197,13 +192,6 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path,
|
system_path,
|
||||||
user_path: None,
|
user_path: None,
|
||||||
previous: if version > 1 && look_for_previous {
|
|
||||||
Self::system_inner(name, version - 1, false)
|
|
||||||
.ok()
|
|
||||||
.map(Box::new)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -211,10 +199,6 @@ impl Config {
|
||||||
// Use folder at XDG config/name for config storage, return Config if successful
|
// 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)
|
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||||
pub fn new(name: &str, version: u64) -> Result<Self, Error> {
|
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]
|
// Look for [name]/v[version]
|
||||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||||
|
|
||||||
|
|
@ -239,29 +223,15 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path,
|
system_path,
|
||||||
user_path: Some(user_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.
|
/// 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> {
|
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]
|
// Look for [name]/v[version]
|
||||||
let path = sanitize_name(name)?.join(format!("v{version}"));
|
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||||
|
|
||||||
let mut user_path = custom_path.clone();
|
let mut user_path = custom_path;
|
||||||
user_path.push("cosmic");
|
user_path.push("cosmic");
|
||||||
user_path.push(path);
|
user_path.push(path);
|
||||||
// Create new configuration directory if not found.
|
// Create new configuration directory if not found.
|
||||||
|
|
@ -271,13 +241,6 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path: None,
|
system_path: None,
|
||||||
user_path: Some(user_path),
|
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
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -287,10 +250,6 @@ impl Config {
|
||||||
// Use folder at XDG config/name for config storage, return Config if successful
|
// 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)
|
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||||
pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
|
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]
|
// Look for [name]/v[version]
|
||||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||||
|
|
||||||
|
|
@ -304,13 +263,6 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path: None,
|
system_path: None,
|
||||||
user_path: Some(user_path),
|
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
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -421,13 +373,7 @@ impl ConfigGet for Config {
|
||||||
Ok(ron::from_str(&data)?)
|
Ok(ron::from_str(&data)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => {
|
_ => Err(Error::NotFound),
|
||||||
if let Some(previous) = self.previous.as_ref() {
|
|
||||||
previous.get_local(key)
|
|
||||||
} else {
|
|
||||||
Err(Error::NotFound)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ export = ["serde_json"]
|
||||||
no-default = []
|
no-default = []
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
hex_color = { version = "3", features = ["serde"] }
|
|
||||||
palette = { version = "0.7.6", features = ["serializing"] }
|
palette = { version = "0.7.6", features = ["serializing"] }
|
||||||
almost = "0.2"
|
almost = "0.2"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
|
|
|
||||||
|
|
@ -1,173 +0,0 @@
|
||||||
//! 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::color::color_serde;
|
|
||||||
use palette::Srgba;
|
use palette::Srgba;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
@ -96,107 +95,75 @@ pub struct CosmicPaletteInner {
|
||||||
|
|
||||||
/// Utility Colors
|
/// Utility Colors
|
||||||
/// Colors used for various points of emphasis in the UI.
|
/// Colors used for various points of emphasis in the UI.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub bright_red: Srgba,
|
pub bright_red: Srgba,
|
||||||
/// Colors used for various points of emphasis in the UI.
|
/// Colors used for various points of emphasis in the UI.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub bright_green: Srgba,
|
pub bright_green: Srgba,
|
||||||
/// Colors used for various points of emphasis in the UI.
|
/// Colors used for various points of emphasis in the UI.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub bright_orange: Srgba,
|
pub bright_orange: Srgba,
|
||||||
|
|
||||||
/// Surface Grays
|
/// Surface Grays
|
||||||
/// Colors used for three levels of surfaces in the UI.
|
/// Colors used for three levels of surfaces in the UI.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub gray_1: Srgba,
|
pub gray_1: Srgba,
|
||||||
/// Colors used for three levels of surfaces in the UI.
|
/// Colors used for three levels of surfaces in the UI.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub gray_2: Srgba,
|
pub gray_2: Srgba,
|
||||||
|
|
||||||
/// System Neutrals
|
/// System Neutrals
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_0: Srgba,
|
pub neutral_0: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_1: Srgba,
|
pub neutral_1: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_2: Srgba,
|
pub neutral_2: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_3: Srgba,
|
pub neutral_3: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_4: Srgba,
|
pub neutral_4: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_5: Srgba,
|
pub neutral_5: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_6: Srgba,
|
pub neutral_6: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_7: Srgba,
|
pub neutral_7: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_8: Srgba,
|
pub neutral_8: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_9: Srgba,
|
pub neutral_9: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub neutral_10: Srgba,
|
pub neutral_10: Srgba,
|
||||||
|
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_blue: Srgba,
|
pub accent_blue: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_indigo: Srgba,
|
pub accent_indigo: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_purple: Srgba,
|
pub accent_purple: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_pink: Srgba,
|
pub accent_pink: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_red: Srgba,
|
pub accent_red: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_orange: Srgba,
|
pub accent_orange: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_yellow: Srgba,
|
pub accent_yellow: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_green: Srgba,
|
pub accent_green: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub accent_warm_grey: Srgba,
|
pub accent_warm_grey: Srgba,
|
||||||
|
|
||||||
/// Extended Color Palette
|
/// Extended Color Palette
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub ext_warm_grey: Srgba,
|
pub ext_warm_grey: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub ext_orange: Srgba,
|
pub ext_orange: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub ext_yellow: Srgba,
|
pub ext_yellow: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub ext_blue: Srgba,
|
pub ext_blue: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub ext_purple: Srgba,
|
pub ext_purple: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub ext_pink: Srgba,
|
pub ext_pink: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub ext_indigo: Srgba,
|
pub ext_indigo: Srgba,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
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",))
|
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)))
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
use crate::color::color_serde;
|
|
||||||
use palette::{Srgba, WithAlpha};
|
use palette::{Srgba, WithAlpha};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -9,18 +8,14 @@ use crate::composite::over;
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
/// the color of the container
|
/// the color of the container
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub base: Srgba,
|
pub base: Srgba,
|
||||||
/// the color of components in the container
|
/// the color of components in the container
|
||||||
pub component: Component,
|
pub component: Component,
|
||||||
/// the color of dividers in the container
|
/// the color of dividers in the container
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub divider: Srgba,
|
pub divider: Srgba,
|
||||||
/// the color of text in the container
|
/// the color of text in the container
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub on: Srgba,
|
pub on: Srgba,
|
||||||
/// the color of @small_widget_container
|
/// the color of @small_widget_container
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub small_widget: Srgba,
|
pub small_widget: Srgba,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -50,42 +45,30 @@ impl Container {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Component {
|
pub struct Component {
|
||||||
/// The base color of the widget
|
/// The base color of the widget
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub base: Srgba,
|
pub base: Srgba,
|
||||||
/// The color of the widget when it is hovered
|
/// The color of the widget when it is hovered
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub hover: Srgba,
|
pub hover: Srgba,
|
||||||
/// the color of the widget when it is pressed
|
/// the color of the widget when it is pressed
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub pressed: Srgba,
|
pub pressed: Srgba,
|
||||||
/// the color of the widget when it is selected
|
/// the color of the widget when it is selected
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub selected: Srgba,
|
pub selected: Srgba,
|
||||||
/// the color of the widget when it is selected
|
/// the color of the widget when it is selected
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub selected_text: Srgba,
|
pub selected_text: Srgba,
|
||||||
/// the color of the widget when it is focused
|
/// the color of the widget when it is focused
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub focus: Srgba,
|
pub focus: Srgba,
|
||||||
/// the color of dividers for this widget
|
/// the color of dividers for this widget
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub divider: Srgba,
|
pub divider: Srgba,
|
||||||
/// the color of text for this widget
|
/// the color of text for this widget
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub on: Srgba,
|
pub on: Srgba,
|
||||||
// the color of text with opacity 80 for this widget
|
// the color of text with opacity 80 for this widget
|
||||||
// pub text_opacity_80: Srgba,
|
// pub text_opacity_80: Srgba,
|
||||||
/// the color of the widget when it is disabled
|
/// the color of the widget when it is disabled
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub disabled: Srgba,
|
pub disabled: Srgba,
|
||||||
/// the color of text in the widget when it is disabled
|
/// the color of text in the widget when it is disabled
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub on_disabled: Srgba,
|
pub on_disabled: Srgba,
|
||||||
/// the color of the border for the widget
|
/// the color of the border for the widget
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub border: Srgba,
|
pub border: Srgba,
|
||||||
/// the color of the border for the widget when it is disabled
|
/// the color of the border for the widget when it is disabled
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
pub disabled_border: Srgba,
|
pub disabled_border: Srgba,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
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",))
|
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)))
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ pub use mode::*;
|
||||||
pub use spacing::*;
|
pub use spacing::*;
|
||||||
pub use theme::*;
|
pub use theme::*;
|
||||||
|
|
||||||
pub mod color;
|
|
||||||
mod corner;
|
mod corner;
|
||||||
mod cosmic_palette;
|
mod cosmic_palette;
|
||||||
mod density;
|
mod density;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,15 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Component, Container, CornerRadii, CosmicPalette, CosmicPaletteInner, DARK_PALETTE,
|
Component, Container, CornerRadii, CosmicPalette, CosmicPaletteInner, DARK_PALETTE,
|
||||||
LIGHT_PALETTE, NAME, Spacing, ThemeMode,
|
LIGHT_PALETTE, NAME, Spacing, ThemeMode,
|
||||||
color::{ColorRepr, ColorReprOption, color_serde, color_serde::option as color_serde_option},
|
|
||||||
composite::over,
|
composite::over,
|
||||||
steps::{color_index, get_small_widget_color, get_surface_color, get_text, steps},
|
steps::{color_index, get_small_widget_color, get_surface_color, get_text, steps},
|
||||||
};
|
};
|
||||||
use cosmic_config::{Config, CosmicConfigEntry, cosmic_config_derive::CosmicConfigEntry};
|
use cosmic_config::{Config, CosmicConfigEntry};
|
||||||
use palette::{
|
use palette::{
|
||||||
IntoColor, Oklcha, Srgb, Srgba, WithAlpha, color_difference::Wcag21RelativeContrast, rgb::Rgb,
|
IntoColor, Oklcha, Srgb, Srgba, WithAlpha, color_difference::Wcag21RelativeContrast, rgb::Rgb,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::{default, num::NonZeroUsize};
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
/// ID for the current dark `ThemeBuilder` config
|
/// ID for the current dark `ThemeBuilder` config
|
||||||
pub const DARK_THEME_BUILDER_ID: &str = "com.system76.CosmicTheme.Dark.Builder";
|
pub const DARK_THEME_BUILDER_ID: &str = "com.system76.CosmicTheme.Dark.Builder";
|
||||||
|
|
@ -38,25 +37,24 @@ pub enum Layer {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// Cosmic Theme data structure with all colors and its name
|
/// Cosmic Theme data structure with all colors and its name
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, CosmicConfigEntry)]
|
#[derive(
|
||||||
#[version = 2]
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
PartialEq,
|
||||||
|
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||||
|
)]
|
||||||
|
#[version = 1]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
/// name of the theme
|
/// name of the theme
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// background element colors
|
/// background element colors
|
||||||
pub(crate) background: Container,
|
pub background: Container,
|
||||||
/// primary element colors
|
/// primary element colors
|
||||||
pub(crate) primary: Container,
|
pub primary: Container,
|
||||||
/// secondary element colors
|
/// secondary element colors
|
||||||
pub(crate) secondary: Container,
|
pub 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
|
/// accent element colors
|
||||||
pub accent: Component,
|
pub accent: Component,
|
||||||
/// suggested element colors
|
/// suggested element colors
|
||||||
|
|
@ -79,6 +77,8 @@ pub struct Theme {
|
||||||
pub link_button: Component,
|
pub link_button: Component,
|
||||||
/// text button element colors
|
/// text button element colors
|
||||||
pub text_button: Component,
|
pub text_button: Component,
|
||||||
|
/// button component styling
|
||||||
|
pub button: Component,
|
||||||
/// palette
|
/// palette
|
||||||
pub palette: CosmicPaletteInner,
|
pub palette: CosmicPaletteInner,
|
||||||
/// spacing
|
/// spacing
|
||||||
|
|
@ -96,31 +96,15 @@ pub struct Theme {
|
||||||
/// cosmic-comp custom window hint color
|
/// cosmic-comp custom window hint color
|
||||||
pub window_hint: Option<Srgb>,
|
pub window_hint: Option<Srgb>,
|
||||||
/// enables blurred transparency
|
/// enables blurred transparency
|
||||||
pub frosted: BlurStrength,
|
pub is_frosted: bool,
|
||||||
/// 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
|
/// shade color for dialogs
|
||||||
#[serde(with = "color_serde")]
|
|
||||||
#[cosmic_config_entry(with = ColorRepr)]
|
|
||||||
pub shade: Srgba,
|
pub shade: Srgba,
|
||||||
/// accent text colors
|
/// accent text colors
|
||||||
/// If None, accent base color is the accent text color.
|
/// 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>,
|
pub accent_text: Option<Srgba>,
|
||||||
/// control tint color
|
/// control tint color
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub control_tint: Option<Srgb>,
|
pub control_tint: Option<Srgb>,
|
||||||
/// text tint color
|
/// text tint color
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub text_tint: Option<Srgb>,
|
pub text_tint: Option<Srgb>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -186,39 +170,6 @@ impl Theme {
|
||||||
todo!();
|
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]
|
#[must_use]
|
||||||
#[allow(clippy::doc_markdown)]
|
#[allow(clippy::doc_markdown)]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -788,7 +739,7 @@ impl Theme {
|
||||||
if color_scheme.trim().contains("default") || color_scheme.trim().contains("light") {
|
if color_scheme.trim().contains("default") || color_scheme.trim().contains("light") {
|
||||||
return Self::light_default();
|
return Self::light_default();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
Self::dark_default()
|
Self::dark_default()
|
||||||
}
|
}
|
||||||
|
|
@ -797,10 +748,10 @@ impl Theme {
|
||||||
pub fn preferred_theme() -> Self {
|
pub fn preferred_theme() -> Self {
|
||||||
let current_desktop = std::env::var("XDG_CURRENT_DESKTOP");
|
let current_desktop = std::env::var("XDG_CURRENT_DESKTOP");
|
||||||
|
|
||||||
if let Ok(desktop) = current_desktop
|
if let Ok(desktop) = current_desktop {
|
||||||
&& desktop.trim().to_lowercase().contains("gnome")
|
if desktop.trim().to_lowercase().contains("gnome") {
|
||||||
{
|
return Self::gtk_prefer_colorscheme();
|
||||||
return Self::gtk_prefer_colorscheme();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::dark_default()
|
Self::dark_default()
|
||||||
|
|
@ -815,8 +766,15 @@ impl From<CosmicPalette> for Theme {
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
/// Helper for building customized themes
|
/// Helper for building customized themes
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, CosmicConfigEntry, PartialEq)]
|
#[derive(
|
||||||
#[version = 2]
|
Clone,
|
||||||
|
Debug,
|
||||||
|
Serialize,
|
||||||
|
Deserialize,
|
||||||
|
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||||
|
PartialEq,
|
||||||
|
)]
|
||||||
|
#[version = 1]
|
||||||
pub struct ThemeBuilder {
|
pub struct ThemeBuilder {
|
||||||
/// override the palette for the builder
|
/// override the palette for the builder
|
||||||
pub palette: CosmicPalette,
|
pub palette: CosmicPalette,
|
||||||
|
|
@ -825,59 +783,31 @@ pub struct ThemeBuilder {
|
||||||
/// override corner radii for the builder
|
/// override corner radii for the builder
|
||||||
pub corner_radii: CornerRadii,
|
pub corner_radii: CornerRadii,
|
||||||
/// override neutral_tint for the builder
|
/// override neutral_tint for the builder
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub neutral_tint: Option<Srgb>,
|
pub neutral_tint: Option<Srgb>,
|
||||||
/// override bg_color for the builder
|
/// override bg_color for the builder
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub bg_color: Option<Srgba>,
|
pub bg_color: Option<Srgba>,
|
||||||
/// override the primary container bg color for the builder
|
/// 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>,
|
pub primary_container_bg: Option<Srgba>,
|
||||||
/// override the secontary container bg color for the builder
|
/// 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>,
|
pub secondary_container_bg: Option<Srgba>,
|
||||||
/// override the text tint for the builder
|
/// override the text tint for the builder
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub text_tint: Option<Srgb>,
|
pub text_tint: Option<Srgb>,
|
||||||
/// override the accent color for the builder
|
/// override the accent color for the builder
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub accent: Option<Srgb>,
|
pub accent: Option<Srgb>,
|
||||||
/// override the success color for the builder
|
/// override the success color for the builder
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub success: Option<Srgb>,
|
pub success: Option<Srgb>,
|
||||||
/// override the warning color for the builder
|
/// override the warning color for the builder
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub warning: Option<Srgb>,
|
pub warning: Option<Srgb>,
|
||||||
/// override the destructive color for the builder
|
/// override the destructive color for the builder
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub destructive: Option<Srgb>,
|
pub destructive: Option<Srgb>,
|
||||||
/// enabled blurred transparency
|
/// enabled blurred transparency
|
||||||
pub frosted: BlurStrength,
|
pub is_frosted: bool, // TODO handle
|
||||||
/// cosmic-comp window gaps size (outer, inner)
|
/// cosmic-comp window gaps size (outer, inner)
|
||||||
pub gaps: (u32, u32),
|
pub gaps: (u32, u32),
|
||||||
/// cosmic-comp active hint window outline width
|
/// cosmic-comp active hint window outline width
|
||||||
pub active_hint: u32,
|
pub active_hint: u32,
|
||||||
/// cosmic-comp custom window hint color
|
/// cosmic-comp custom window hint color
|
||||||
#[serde(with = "color_serde_option")]
|
|
||||||
#[cosmic_config_entry(with = ColorReprOption)]
|
|
||||||
pub window_hint: Option<Srgb>,
|
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 {
|
impl Default for ThemeBuilder {
|
||||||
|
|
@ -895,15 +825,11 @@ impl Default for ThemeBuilder {
|
||||||
success: Default::default(),
|
success: Default::default(),
|
||||||
warning: Default::default(),
|
warning: Default::default(),
|
||||||
destructive: Default::default(),
|
destructive: Default::default(),
|
||||||
frosted: BlurStrength::default(),
|
is_frosted: false,
|
||||||
// cosmic-comp theme settings
|
// cosmic-comp theme settings
|
||||||
gaps: (0, 8),
|
gaps: (0, 8),
|
||||||
active_hint: 3,
|
active_hint: 3,
|
||||||
window_hint: None,
|
window_hint: None,
|
||||||
frosted_windows: false,
|
|
||||||
frosted_system_interface: false,
|
|
||||||
frosted_panel: false,
|
|
||||||
frosted_applets: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1045,15 +971,9 @@ impl ThemeBuilder {
|
||||||
gaps,
|
gaps,
|
||||||
active_hint,
|
active_hint,
|
||||||
window_hint,
|
window_hint,
|
||||||
frosted,
|
is_frosted,
|
||||||
frosted_windows,
|
|
||||||
frosted_system_interface,
|
|
||||||
frosted_panel,
|
|
||||||
frosted_applets,
|
|
||||||
} = self;
|
} = self;
|
||||||
|
|
||||||
let container_alpha = frosted.alpha();
|
|
||||||
|
|
||||||
let is_dark = palette.is_dark();
|
let is_dark = palette.is_dark();
|
||||||
let is_high_contrast = palette.is_high_contrast();
|
let is_high_contrast = palette.is_high_contrast();
|
||||||
|
|
||||||
|
|
@ -1108,10 +1028,6 @@ impl ThemeBuilder {
|
||||||
let step_array = steps(bg, NonZeroUsize::new(100).unwrap());
|
let step_array = steps(bg, NonZeroUsize::new(100).unwrap());
|
||||||
let bg_index = color_index(bg, step_array.len());
|
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 {
|
let mut component_hovered_overlay = if bg_index < 91 {
|
||||||
control_steps_array[10]
|
control_steps_array[10]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1138,22 +1054,8 @@ impl ThemeBuilder {
|
||||||
text_steps_array.as_deref(),
|
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 primary = {
|
||||||
let mut container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
|
let container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
|
||||||
primary_container_bg_color
|
primary_container_bg_color
|
||||||
} else {
|
} else {
|
||||||
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
|
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
|
||||||
|
|
@ -1200,45 +1102,6 @@ impl ThemeBuilder {
|
||||||
is_high_contrast,
|
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 {
|
let accent_text = if is_dark {
|
||||||
(primary.base.relative_contrast(accent.color) < 4.).then(|| {
|
(primary.base.relative_contrast(accent.color) < 4.).then(|| {
|
||||||
|
|
@ -1469,75 +1332,10 @@ impl ThemeBuilder {
|
||||||
gaps,
|
gaps,
|
||||||
active_hint,
|
active_hint,
|
||||||
window_hint,
|
window_hint,
|
||||||
frosted,
|
is_frosted,
|
||||||
accent_text,
|
accent_text,
|
||||||
control_tint: neutral_tint,
|
control_tint: neutral_tint,
|
||||||
text_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.spacing = spacing;
|
||||||
theme.corner_radii = corner_radii;
|
theme.corner_radii = corner_radii;
|
||||||
|
|
@ -1556,73 +1354,3 @@ impl ThemeBuilder {
|
||||||
Config::new(LIGHT_THEME_BUILDER_ID, Self::VERSION)
|
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(()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ ForegroundPositive=0,87,44
|
||||||
ForegroundVisited=0,82,90
|
ForegroundVisited=0,82,90
|
||||||
|
|
||||||
[Colors:Selection]
|
[Colors:Selection]
|
||||||
BackgroundAlternate=108,149,153
|
BackgroundAlternate=108,149,152
|
||||||
BackgroundNormal=0,82,90
|
BackgroundNormal=0,82,90
|
||||||
DecorationFocus=0,82,90
|
DecorationFocus=0,82,90
|
||||||
DecorationHover=0,82,90
|
DecorationHover=0,82,90
|
||||||
|
|
|
||||||
|
|
@ -21,4 +21,5 @@ features = [
|
||||||
"single-instance",
|
"single-instance",
|
||||||
"surface-message",
|
"surface-message",
|
||||||
"multi-window",
|
"multi-window",
|
||||||
|
"wgpu",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,7 @@ impl cosmic::Application for App {
|
||||||
.map_or("No page selected", String::as_str);
|
.map_or("No page selected", String::as_str);
|
||||||
|
|
||||||
let centered = widget::container(
|
let centered = widget::container(
|
||||||
widget::column::with_capacity(5)
|
widget::column::with_capacity(14)
|
||||||
.push(widget::text::body(page_content))
|
.push(widget::text::body(page_content))
|
||||||
.push(
|
.push(
|
||||||
widget::text_input::text_input("", &self.input_1)
|
widget::text_input::text_input("", &self.input_1)
|
||||||
|
|
@ -223,6 +223,7 @@ impl cosmic::Application for App {
|
||||||
.on_clear(Message::Ignore),
|
.on_clear(Message::Ignore),
|
||||||
)
|
)
|
||||||
.push(widget::progress_bar::circular::Circular::new().size(50.0))
|
.push(widget::progress_bar::circular::Circular::new().size(50.0))
|
||||||
|
.push(widget::progress_bar::circular::Circular::new().size(20.0))
|
||||||
.push(
|
.push(
|
||||||
widget::progress_bar::linear::Linear::new()
|
widget::progress_bar::linear::Linear::new()
|
||||||
.girth(10.0)
|
.girth(10.0)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ links = Links
|
||||||
developers = Entwickler(innen)
|
developers = Entwickler(innen)
|
||||||
designers = Designer(innen)
|
designers = Designer(innen)
|
||||||
artists = Künstler(innen)
|
artists = Künstler(innen)
|
||||||
translators = Übersetzer*innen
|
translators = Übersetzer(innen)
|
||||||
documenters = Dokumentierer(innen)
|
documenters = Dokumentierer(innen)
|
||||||
# Calendar
|
# Calendar
|
||||||
january = Januar { $year }
|
january = Januar { $year }
|
||||||
|
|
|
||||||
0
i18n/eu/libcosmic.ftl
Normal file
0
i18n/eu/libcosmic.ftl
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
close = Mdel
|
||||||
|
license = Turagt
|
||||||
|
links = Iseɣwan
|
||||||
|
developers = Ineflayen
|
||||||
|
artists = Inaẓuren
|
||||||
|
translators = Imsuqlen
|
||||||
|
january = Yennayer { $year }
|
||||||
|
february = Fuṛar { $year }
|
||||||
|
march = Meɣres { $year }
|
||||||
|
april = Yebrir { $year }
|
||||||
|
may = Mayyu { $year }
|
||||||
|
june = Yunyu { $year }
|
||||||
|
july = Yulyu { $year }
|
||||||
|
august = Ɣuct { $year }
|
||||||
|
september = Ctembeṛ { $year }
|
||||||
|
october = Tubeṛ { $year }
|
||||||
|
november = Wambeṛ { $year }
|
||||||
|
december = Dujembeṛ { $year }
|
||||||
|
documenters = Imeskaren
|
||||||
|
monday = Arim
|
||||||
|
mon = Ari
|
||||||
|
tuesday = Aram
|
||||||
|
tue = Ara
|
||||||
|
wednesday = Ahad
|
||||||
|
wed = Aha
|
||||||
|
thursday = Amhad
|
||||||
|
thu = Amh
|
||||||
|
friday = Sem
|
||||||
|
fri = Sm
|
||||||
|
saturday = Sed
|
||||||
|
sat = Sd
|
||||||
|
sunday = Acer
|
||||||
|
sun = Ace
|
||||||
|
|
@ -2,26 +2,33 @@ february = { $year }년 2월
|
||||||
close = 닫기
|
close = 닫기
|
||||||
documenters = 문서 작성자
|
documenters = 문서 작성자
|
||||||
november = { $year }년 11월
|
november = { $year }년 11월
|
||||||
friday = 금
|
friday = 금요일
|
||||||
tuesday = 화
|
tuesday = 화요일
|
||||||
may = { $year }년 5월
|
may = { $year }년 5월
|
||||||
wednesday = 수
|
wednesday = 수요일
|
||||||
april = { $year }년 4월
|
april = { $year }년 4월
|
||||||
monday = 월
|
monday = 월요일
|
||||||
translators = 번역가
|
translators = 번역가
|
||||||
artists = 아티스트
|
artists = 아티스트
|
||||||
license = 라이선스
|
license = 라이선스
|
||||||
december = { $year }년 12월
|
december = { $year }년 12월
|
||||||
sunday = 일
|
sunday = 일요일
|
||||||
links = 링크
|
links = 링크
|
||||||
march = { $year }년 3월
|
march = { $year }년 3월
|
||||||
june = { $year }년 6월
|
june = { $year }년 6월
|
||||||
saturday = 토
|
saturday = 토요일
|
||||||
august = { $year }년 8월
|
august = { $year }년 8월
|
||||||
developers = 개발자
|
developers = 개발자
|
||||||
july = { $year }년 7월
|
july = { $year }년 7월
|
||||||
thursday = 목
|
thursday = 목요일
|
||||||
september = { $year }년 9월
|
september = { $year }년 9월
|
||||||
designers = 디자이너
|
designers = 디자이너
|
||||||
october = { $year }년 10월
|
october = { $year }년 10월
|
||||||
january = { $year }년 1월
|
january = { $year }년 1월
|
||||||
|
mon = 월
|
||||||
|
tue = 화
|
||||||
|
wed = 수
|
||||||
|
thu = 목
|
||||||
|
fri = 금
|
||||||
|
sat = 토
|
||||||
|
sun = 일
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
close = 關閉
|
||||||
|
developers = 開發人員
|
||||||
|
designers = 設計人員
|
||||||
|
artists = 美編設計
|
||||||
|
translators = 翻譯人員
|
||||||
|
documenters = 文件編輯人員
|
||||||
|
january = { $year } 年 1 月
|
||||||
|
monday = 星期一
|
||||||
|
tuesday = 星期二
|
||||||
|
wednesday = 星期三
|
||||||
|
thursday = 星期四
|
||||||
|
friday = 星期五
|
||||||
|
saturday = 星期六
|
||||||
|
sunday = 星期日
|
||||||
|
mon = 週一
|
||||||
|
tue = 週二
|
||||||
|
wed = 週三
|
||||||
|
thu = 週四
|
||||||
|
fri = 週五
|
||||||
|
sat = 週六
|
||||||
|
sun = 週日
|
||||||
|
license = 授權
|
||||||
|
links = 連結
|
||||||
|
february = { $year } 年 2 月
|
||||||
|
march = { $year } 年 3 月
|
||||||
|
april = { $year } 年 4 月
|
||||||
|
may = { $year } 年 5 月
|
||||||
|
june = { $year } 年 6 月
|
||||||
|
july = { $year } 年 7 月
|
||||||
|
august = { $year } 年 8 月
|
||||||
|
september = { $year } 年 9 月
|
||||||
|
october = { $year } 年 10 月
|
||||||
|
november = { $year } 年 11 月
|
||||||
|
december = { $year } 年 12 月
|
||||||
2
iced
2
iced
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0a093b3ab0d5ad1b3ad6b457c1715880276e0ce1
|
Subproject commit 78caabba7ef91cd1030da6f70b41d266704ffece
|
||||||
|
|
@ -64,8 +64,6 @@ pub enum Action {
|
||||||
Unfocus(iced::window::Id),
|
Unfocus(iced::window::Id),
|
||||||
/// Windowing system initialized
|
/// Windowing system initialized
|
||||||
WindowingSystemInitialized,
|
WindowingSystemInitialized,
|
||||||
/// Blur support enabled
|
|
||||||
BlurEnabled,
|
|
||||||
/// Updates the window maximized state
|
/// Updates the window maximized state
|
||||||
WindowMaximized(iced::window::Id, bool),
|
WindowMaximized(iced::window::Id, bool),
|
||||||
/// Updates the tracked window geometry.
|
/// Updates the tracked window geometry.
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ use std::collections::{HashMap, HashSet};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use super::{Action, Application, ApplicationExt, Subscription};
|
use super::{Action, Application, ApplicationExt, Subscription};
|
||||||
use crate::core::AppType;
|
|
||||||
use crate::theme::{THEME, Theme, ThemeType};
|
use crate::theme::{THEME, Theme, ThemeType};
|
||||||
use crate::{Core, Element, keyboard_nav};
|
use crate::{Core, Element, keyboard_nav};
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
|
|
@ -95,7 +94,6 @@ pub struct Cosmic<App: Application> {
|
||||||
>,
|
>,
|
||||||
pub tracked_windows: HashSet<window::Id>,
|
pub tracked_windows: HashSet<window::Id>,
|
||||||
pub opened_surfaces: HashMap<window::Id, u32>,
|
pub opened_surfaces: HashMap<window::Id, u32>,
|
||||||
blur_enabled: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Application> Cosmic<T>
|
impl<T: Application> Cosmic<T>
|
||||||
|
|
@ -117,8 +115,8 @@ where
|
||||||
(
|
(
|
||||||
Self::new(model),
|
Self::new(model),
|
||||||
Task::batch([
|
Task::batch([
|
||||||
iced_runtime::window::run_with_handle(id, init_windowing_system),
|
|
||||||
command,
|
command,
|
||||||
|
iced_runtime::window::run_with_handle(id, init_windowing_system),
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -462,9 +460,6 @@ where
|
||||||
)) => {
|
)) => {
|
||||||
return Some(Action::WindowState(id, s));
|
return Some(Action::WindowState(id, s));
|
||||||
}
|
}
|
||||||
wayland::Event::BlurEnabled => {
|
|
||||||
return Some(Action::BlurEnabled);
|
|
||||||
}
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -678,13 +673,34 @@ impl<T: Application> Cosmic<T> {
|
||||||
state.intersects(WindowState::MAXIMIZED | WindowState::FULLSCREEN);
|
state.intersects(WindowState::MAXIMIZED | WindowState::FULLSCREEN);
|
||||||
}
|
}
|
||||||
if self.app.core().sync_window_border_radii_to_theme() {
|
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;
|
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
||||||
|
|
||||||
let theme = THEME.lock().unwrap();
|
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;
|
let rounded = !self.app.core().window.sharp_corners;
|
||||||
|
return Task::batch([corner_radius(
|
||||||
let cur_rad = self.app.core().app_type.corners(&theme, rounded);
|
id,
|
||||||
return Task::batch([corner_radius(id, Some(cur_rad)).discard()]);
|
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()]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -757,61 +773,18 @@ impl<T: Application> Cosmic<T> {
|
||||||
if a.distance_squared(*t_inner.accent_color()) > 0.00001 {
|
if a.distance_squared(*t_inner.accent_color()) > 0.00001 {
|
||||||
theme = Theme::system(Arc::new(t_inner.with_accent(a)));
|
theme = Theme::system(Arc::new(t_inner.with_accent(a)));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
THEME.lock().unwrap().set_theme(theme.theme_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
Action::SystemThemeChange(keys, mut theme) => {
|
Action::SystemThemeChange(keys, theme) => {
|
||||||
let cur_is_dark = THEME.lock().unwrap().theme_type.is_dark();
|
let cur_is_dark = THEME.lock().unwrap().theme_type.is_dark();
|
||||||
// Ignore updates if the current theme mode does not match.
|
// Ignore updates if the current theme mode does not match.
|
||||||
if cur_is_dark != theme.cosmic().is_dark {
|
if cur_is_dark != theme.cosmic().is_dark {
|
||||||
return iced::Task::none();
|
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());
|
let cmd = self.app.system_theme_update(&keys, theme.cosmic());
|
||||||
// Record the last-known system theme in event that the current theme is custom.
|
// Record the last-known system theme in event that the current theme is custom.
|
||||||
self.app.core_mut().system_theme = theme.clone();
|
self.app.core_mut().system_theme = theme.clone();
|
||||||
|
|
@ -835,38 +808,89 @@ impl<T: Application> Cosmic<T> {
|
||||||
} else {
|
} else {
|
||||||
theme
|
theme
|
||||||
};
|
};
|
||||||
new_theme.transparent = new_blur;
|
|
||||||
new_theme.theme_type.prefer_dark(prefer_dark);
|
new_theme.theme_type.prefer_dark(prefer_dark);
|
||||||
|
|
||||||
cosmic_theme.set_theme(new_theme.theme_type);
|
cosmic_theme.set_theme(new_theme.theme_type);
|
||||||
cosmic_theme.transparent = new_blur;
|
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
if self.app.core().sync_window_border_radii_to_theme() {
|
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;
|
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
||||||
|
|
||||||
let t = cosmic_theme.cosmic();
|
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 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
|
// Update radius for the main window
|
||||||
let main_window_id = self
|
let main_window_id = self
|
||||||
.app
|
.app
|
||||||
.core()
|
.core()
|
||||||
.main_window_id()
|
.main_window_id()
|
||||||
.unwrap_or(window::Id::RESERVED);
|
.unwrap_or(window::Id::RESERVED);
|
||||||
let mut cmds =
|
let mut cmds = vec![
|
||||||
vec![corner_radius(main_window_id, Some(cur_rad)).discard()];
|
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(),
|
||||||
|
];
|
||||||
// Update radius for each tracked view with the window surface type
|
// Update radius for each tracked view with the window surface type
|
||||||
for (id, (_, surface_type, _)) in self.surface_views.iter() {
|
for (id, (_, surface_type, _)) in self.surface_views.iter() {
|
||||||
let cur_rad = corners(*surface_type, rounded, &cosmic_theme);
|
if let SurfaceIdWrapper::Window(_) = surface_type {
|
||||||
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Update radius for all tracked windows
|
// Update radius for all tracked windows
|
||||||
for id in &self.tracked_windows {
|
for id in self.tracked_windows.iter() {
|
||||||
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
|
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(),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task::batch(cmds);
|
return Task::batch(cmds);
|
||||||
|
|
@ -897,7 +921,6 @@ impl<T: Application> Cosmic<T> {
|
||||||
} {
|
} {
|
||||||
return iced::Task::none();
|
return iced::Task::none();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut cmds = vec![self.app.system_theme_mode_update(&keys, &mode)];
|
let mut cmds = vec![self.app.system_theme_mode_update(&keys, &mode)];
|
||||||
|
|
||||||
let core = self.app.core_mut();
|
let core = self.app.core_mut();
|
||||||
|
|
@ -926,15 +949,6 @@ impl<T: Application> Cosmic<T> {
|
||||||
} else {
|
} else {
|
||||||
new_theme
|
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();
|
core.system_theme = new_theme.clone();
|
||||||
{
|
{
|
||||||
|
|
@ -943,14 +957,21 @@ impl<T: Application> Cosmic<T> {
|
||||||
// Only apply update if the theme is set to load a system theme
|
// Only apply update if the theme is set to load a system theme
|
||||||
if let ThemeType::System { .. } = cosmic_theme.theme_type {
|
if let ThemeType::System { .. } = cosmic_theme.theme_type {
|
||||||
cosmic_theme.set_theme(new_theme.theme_type);
|
cosmic_theme.set_theme(new_theme.theme_type);
|
||||||
cosmic_theme.transparent = new_blur;
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
if self.app.core().sync_window_border_radii_to_theme() {
|
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;
|
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 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
|
// Update radius for the main window
|
||||||
let main_window_id = self
|
let main_window_id = self
|
||||||
|
|
@ -958,35 +979,64 @@ impl<T: Application> Cosmic<T> {
|
||||||
.core()
|
.core()
|
||||||
.main_window_id()
|
.main_window_id()
|
||||||
.unwrap_or(window::Id::RESERVED);
|
.unwrap_or(window::Id::RESERVED);
|
||||||
let mut cmds =
|
let mut cmds = vec![
|
||||||
vec![corner_radius(main_window_id, Some(cur_rad)).discard()];
|
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(),
|
||||||
|
];
|
||||||
// Update radius for each tracked view with the window surface type
|
// Update radius for each tracked view with the window surface type
|
||||||
for (id, (_, surface_type, _)) in self.surface_views.iter() {
|
for (id, (_, surface_type, _)) in self.surface_views.iter() {
|
||||||
let cur_rad = corners(*surface_type, rounded, &cosmic_theme);
|
if let SurfaceIdWrapper::Window(_) = surface_type {
|
||||||
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
|
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(),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Update radius for all tracked windows
|
// Update radius for all tracked windows
|
||||||
for id in &self.tracked_windows {
|
for id in self.tracked_windows.iter() {
|
||||||
cmds.push(corner_radius(*id, Some(cur_rad)).discard());
|
cmds.push(
|
||||||
}
|
corner_radius(
|
||||||
|
*id,
|
||||||
let core = self.app.core();
|
if rounded {
|
||||||
if core.auto_blur {
|
Some(cur_rad)
|
||||||
let blur = if cosmic_theme.transparent {
|
} else {
|
||||||
iced::window::enable_blur
|
let rad_0 = t.radius_0();
|
||||||
} else {
|
Some(CornerRadius {
|
||||||
iced::window::disable_blur
|
top_left: rad_0[0].round() as u32,
|
||||||
};
|
top_right: rad_0[1].round() as u32,
|
||||||
|
bottom_right: rad_0[2].round() as u32,
|
||||||
cmds.push(blur(
|
bottom_left: rad_0[3].round() as u32,
|
||||||
self.app
|
})
|
||||||
.core()
|
},
|
||||||
.main_window_id()
|
)
|
||||||
.unwrap_or(window::Id::RESERVED),
|
.discard(),
|
||||||
));
|
);
|
||||||
for id in &self.tracked_windows {
|
|
||||||
cmds.push(blur(*id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Task::batch(cmds);
|
return Task::batch(cmds);
|
||||||
|
|
@ -1009,7 +1059,7 @@ impl<T: Application> Cosmic<T> {
|
||||||
#[allow(clippy::used_underscore_binding)]
|
#[allow(clippy::used_underscore_binding)]
|
||||||
_token,
|
_token,
|
||||||
),
|
),
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(all(feature = "wayland", target_os = "linux")))]
|
#[cfg(not(all(feature = "wayland", target_os = "linux")))]
|
||||||
|
|
@ -1086,48 +1136,18 @@ impl<T: Application> Cosmic<T> {
|
||||||
|
|
||||||
if changed {
|
if changed {
|
||||||
core.theme_sub_counter += 1;
|
core.theme_sub_counter += 1;
|
||||||
let mut new_theme = if is_dark {
|
let new_theme = if is_dark {
|
||||||
crate::theme::system_dark()
|
crate::theme::system_dark()
|
||||||
} else {
|
} else {
|
||||||
crate::theme::system_light()
|
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();
|
core.system_theme = new_theme.clone();
|
||||||
{
|
{
|
||||||
let mut cosmic_theme = THEME.lock().unwrap();
|
let mut cosmic_theme = THEME.lock().unwrap();
|
||||||
|
|
||||||
// Only apply update if the theme is set to load a system theme
|
// Only apply update if the theme is set to load a system theme
|
||||||
if let ThemeType::System { theme: _, .. } = cosmic_theme.theme_type {
|
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);
|
cosmic_theme.set_theme(new_theme.theme_type);
|
||||||
return Task::batch(cmds);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1229,91 +1249,43 @@ impl<T: Application> Cosmic<T> {
|
||||||
Action::Opened(id) => {
|
Action::Opened(id) => {
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
if self.app.core().sync_window_border_radii_to_theme() {
|
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;
|
use iced_winit::platform_specific::commands::corner_radius::corner_radius;
|
||||||
|
|
||||||
let mut theme = THEME.lock().unwrap();
|
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,
|
||||||
|
};
|
||||||
// TODO do we need per window sharp corners?
|
// TODO do we need per window sharp corners?
|
||||||
let rounded = !self.app.core().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([
|
return Task::batch([
|
||||||
blur_cmd,
|
corner_radius(
|
||||||
corner_task,
|
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(),
|
||||||
iced_runtime::window::run_with_handle(id, init_windowing_system),
|
iced_runtime::window::run_with_handle(id, init_windowing_system),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
return 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()
|
iced::Task::none()
|
||||||
|
|
@ -1328,7 +1300,6 @@ impl<App: Application> Cosmic<App> {
|
||||||
surface_views: HashMap::new(),
|
surface_views: HashMap::new(),
|
||||||
tracked_windows: HashSet::new(),
|
tracked_windows: HashSet::new(),
|
||||||
opened_surfaces: HashMap::new(),
|
opened_surfaces: HashMap::new(),
|
||||||
blur_enabled: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1403,39 +1374,3 @@ impl<App: Application> Cosmic<App> {
|
||||||
.discard()
|
.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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,9 @@ impl<A: crate::app::Application> BootFn<cosmic::Cosmic<A>, crate::Action<A::Mess
|
||||||
///
|
///
|
||||||
/// Returns error on application failure.
|
/// Returns error on application failure.
|
||||||
pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result {
|
pub fn run<App: Application>(settings: Settings, flags: App::Flags) -> iced::Result {
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
image_extras::register();
|
||||||
|
|
||||||
#[cfg(all(target_env = "gnu", not(target_os = "windows")))]
|
#[cfg(all(target_env = "gnu", not(target_os = "windows")))]
|
||||||
if let Some(threshold) = settings.default_mmap_threshold {
|
if let Some(threshold) = settings.default_mmap_threshold {
|
||||||
crate::malloc::limit_mmap_threshold(threshold);
|
crate::malloc::limit_mmap_threshold(threshold);
|
||||||
|
|
@ -194,6 +197,9 @@ where
|
||||||
App::Flags: CosmicFlags,
|
App::Flags: CosmicFlags,
|
||||||
App::Message: Clone + std::fmt::Debug + Send + 'static,
|
App::Message: Clone + std::fmt::Debug + Send + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(feature = "desktop")]
|
||||||
|
image_extras::register();
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
let activation_token = std::env::var("XDG_ACTIVATION_TOKEN").ok();
|
let activation_token = std::env::var("XDG_ACTIVATION_TOKEN").ok();
|
||||||
|
|
@ -820,7 +826,7 @@ impl<App: Application> ApplicationExt for App {
|
||||||
let cosmic = theme.cosmic();
|
let cosmic = theme.cosmic();
|
||||||
container::Style {
|
container::Style {
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(
|
||||||
cosmic.background(theme.transparent).base.into(),
|
cosmic.background.base.into(),
|
||||||
)),
|
)),
|
||||||
border: iced::Border {
|
border: iced::Border {
|
||||||
radius: [
|
radius: [
|
||||||
|
|
@ -849,7 +855,7 @@ impl<App: Application> ApplicationExt for App {
|
||||||
container::Style {
|
container::Style {
|
||||||
background: if content_container {
|
background: if content_container {
|
||||||
Some(iced::Background::Color(
|
Some(iced::Background::Color(
|
||||||
theme.cosmic().background(theme.transparent).base.into(),
|
theme.cosmic().background.base.into(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
||||||
|
|
@ -227,7 +227,7 @@ impl Context {
|
||||||
let icon = widget::icon(icon)
|
let icon = widget::icon(icon)
|
||||||
.class(if symbolic {
|
.class(if symbolic {
|
||||||
theme::Svg::Custom(Rc::new(|theme| iced_widget::svg::Style {
|
theme::Svg::Custom(Rc::new(|theme| iced_widget::svg::Style {
|
||||||
color: Some(theme.cosmic().background(theme.transparent).on.into()),
|
color: Some(theme.cosmic().background.on.into()),
|
||||||
}))
|
}))
|
||||||
} else {
|
} else {
|
||||||
theme::Svg::default()
|
theme::Svg::default()
|
||||||
|
|
@ -378,18 +378,16 @@ impl Context {
|
||||||
Container::<Message, _, Renderer>::new(content).style(|theme| {
|
Container::<Message, _, Renderer>::new(content).style(|theme| {
|
||||||
let cosmic = theme.cosmic();
|
let cosmic = theme.cosmic();
|
||||||
let corners = cosmic.corner_radii;
|
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 {
|
iced_widget::container::Style {
|
||||||
text_color: Some(cosmic.background(theme.transparent).on.into()),
|
text_color: Some(cosmic.background.on.into()),
|
||||||
background: Some(Color::from(bg).into()),
|
background: Some(Color::from(cosmic.background.base).into()),
|
||||||
border: iced::Border {
|
border: iced::Border {
|
||||||
radius: corners.radius_m.into(),
|
radius: corners.radius_m.into(),
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
color: cosmic.background(theme.transparent).divider.into(),
|
color: cosmic.background.divider.into(),
|
||||||
},
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
icon_color: Some(cosmic.background(theme.transparent).on.into()),
|
icon_color: Some(cosmic.background.on.into()),
|
||||||
snap: true,
|
snap: true,
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
@ -567,7 +565,6 @@ pub fn run<App: Application>(flags: App::Flags) -> iced::Result {
|
||||||
core.window.show_maximize = false;
|
core.window.show_maximize = false;
|
||||||
core.window.show_minimize = false;
|
core.window.show_minimize = false;
|
||||||
core.window.use_template = false;
|
core.window.use_template = false;
|
||||||
core.app_type = crate::core::AppType::Applet;
|
|
||||||
|
|
||||||
window_settings.decorations = false;
|
window_settings.decorations = false;
|
||||||
window_settings.exit_on_close_request = true;
|
window_settings.exit_on_close_request = true;
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,7 @@ pub fn file_transfer_send(
|
||||||
/// Returns a list of file paths.
|
/// Returns a list of file paths.
|
||||||
#[cfg(feature = "xdg-portal")]
|
#[cfg(feature = "xdg-portal")]
|
||||||
pub fn file_transfer_receive(key: String) -> iced::Task<ashpd::Result<Vec<String>>> {
|
pub fn file_transfer_receive(key: String) -> iced::Task<ashpd::Result<Vec<String>>> {
|
||||||
|
dbg!(&key);
|
||||||
iced::Task::future(async move {
|
iced::Task::future(async move {
|
||||||
let file_transfer = ashpd::documents::FileTransfer::new().await?;
|
let file_transfer = ashpd::documents::FileTransfer::new().await?;
|
||||||
file_transfer.retrieve_files(&key).await
|
file_transfer.retrieve_files(&key).await
|
||||||
|
|
|
||||||
71
src/core.rs
71
src/core.rs
|
|
@ -101,59 +101,6 @@ pub struct Core {
|
||||||
|
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
pub(crate) sync_window_border_radii_to_theme: bool,
|
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 {
|
impl Default for Core {
|
||||||
|
|
@ -214,8 +161,6 @@ impl Default for Core {
|
||||||
menu_bars: HashMap::new(),
|
menu_bars: HashMap::new(),
|
||||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||||
sync_window_border_radii_to_theme: true,
|
sync_window_border_radii_to_theme: true,
|
||||||
auto_blur: true,
|
|
||||||
app_type: AppType::Window,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -557,20 +502,4 @@ impl Core {
|
||||||
pub fn sync_window_border_radii_to_theme(&self) -> bool {
|
pub fn sync_window_border_radii_to_theme(&self) -> bool {
|
||||||
self.sync_window_border_radii_to_theme
|
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,6 @@ pub static TRANSPARENT_COMPONENT: LazyLock<Component> = LazyLock::new(|| Compone
|
||||||
pub(crate) static THEME: Mutex<Theme> = Mutex::new(Theme {
|
pub(crate) static THEME: Mutex<Theme> = Mutex::new(Theme {
|
||||||
theme_type: ThemeType::Dark,
|
theme_type: ThemeType::Dark,
|
||||||
layer: cosmic_theme::Layer::Background,
|
layer: cosmic_theme::Layer::Background,
|
||||||
transparent: false,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Currently-defined theme.
|
/// Currently-defined theme.
|
||||||
|
|
@ -214,7 +213,6 @@ impl ThemeType {
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
pub theme_type: ThemeType,
|
pub theme_type: ThemeType,
|
||||||
pub layer: cosmic_theme::Layer,
|
pub layer: cosmic_theme::Layer,
|
||||||
pub transparent: bool,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
impl Theme {
|
||||||
|
|
@ -285,9 +283,9 @@ impl Theme {
|
||||||
/// can be used in a component that is intended to be a child of a `CosmicContainer`
|
/// can be used in a component that is intended to be a child of a `CosmicContainer`
|
||||||
pub fn current_container(&self) -> &cosmic_theme::Container {
|
pub fn current_container(&self) -> &cosmic_theme::Container {
|
||||||
match self.layer {
|
match self.layer {
|
||||||
cosmic_theme::Layer::Background => &self.cosmic().background(self.transparent),
|
cosmic_theme::Layer::Background => &self.cosmic().background,
|
||||||
cosmic_theme::Layer::Primary => &self.cosmic().primary(self.transparent),
|
cosmic_theme::Layer::Primary => &self.cosmic().primary,
|
||||||
cosmic_theme::Layer::Secondary => &self.cosmic().secondary(self.transparent),
|
cosmic_theme::Layer::Secondary => &self.cosmic().secondary,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -309,7 +307,7 @@ impl DefaultStyle for Theme {
|
||||||
fn default_style(&self) -> Appearance {
|
fn default_style(&self) -> Appearance {
|
||||||
let cosmic = self.cosmic();
|
let cosmic = self.cosmic();
|
||||||
Appearance {
|
Appearance {
|
||||||
icon_color: cosmic.bg_color().into(),
|
icon_color: cosmic.on_bg_color().into(),
|
||||||
background_color: cosmic.bg_color().into(),
|
background_color: cosmic.bg_color().into(),
|
||||||
text_color: cosmic.on_bg_color().into(),
|
text_color: cosmic.on_bg_color().into(),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,7 +27,7 @@ pub enum Button {
|
||||||
IconVertical,
|
IconVertical,
|
||||||
Image,
|
Image,
|
||||||
Link,
|
Link,
|
||||||
ListItem,
|
ListItem([f32; 4]),
|
||||||
MenuFolder,
|
MenuFolder,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
MenuRoot,
|
MenuRoot,
|
||||||
|
|
@ -128,34 +128,33 @@ pub fn appearance(
|
||||||
let (background, _, _) = color(&cosmic.text_button);
|
let (background, _, _) = color(&cosmic.text_button);
|
||||||
appearance.background = Some(Background::Color(background));
|
appearance.background = Some(Background::Color(background));
|
||||||
|
|
||||||
appearance.icon_color = Some(cosmic.background(theme.transparent).on.into());
|
appearance.icon_color = Some(cosmic.background.on.into());
|
||||||
appearance.text_color = Some(cosmic.background(theme.transparent).on.into());
|
appearance.text_color = Some(cosmic.background.on.into());
|
||||||
corner_radii = &cosmic.corner_radii.radius_0;
|
corner_radii = &cosmic.corner_radii.radius_0;
|
||||||
}
|
}
|
||||||
Button::AppletIcon => {
|
Button::AppletIcon => {
|
||||||
let (background, _, _) = color(&cosmic.text_button);
|
let (background, _, _) = color(&cosmic.text_button);
|
||||||
appearance.background = Some(Background::Color(background));
|
appearance.background = Some(Background::Color(background));
|
||||||
|
|
||||||
appearance.icon_color = Some(cosmic.background(theme.transparent).on.into());
|
appearance.icon_color = Some(cosmic.background.on.into());
|
||||||
appearance.text_color = Some(cosmic.background(theme.transparent).on.into());
|
appearance.text_color = Some(cosmic.background.on.into());
|
||||||
}
|
}
|
||||||
Button::MenuFolder => {
|
Button::MenuFolder => {
|
||||||
// Menu folders cannot be disabled, ignore customized icon and text color
|
// Menu folders cannot be disabled, ignore customized icon and text color
|
||||||
let component = &cosmic.background(theme.transparent).component;
|
let component = &cosmic.background.component;
|
||||||
let (background, _, _) = color(component);
|
let (background, _, _) = color(component);
|
||||||
appearance.background = Some(Background::Color(background));
|
appearance.background = Some(Background::Color(background));
|
||||||
appearance.icon_color = Some(component.on.into());
|
appearance.icon_color = Some(component.on.into());
|
||||||
appearance.text_color = Some(component.on.into());
|
appearance.text_color = Some(component.on.into());
|
||||||
corner_radii = &cosmic.corner_radii.radius_s;
|
corner_radii = &cosmic.corner_radii.radius_s;
|
||||||
}
|
}
|
||||||
Button::ListItem => {
|
Button::ListItem(radii) => {
|
||||||
corner_radii = &[0.0; 4];
|
corner_radii = radii;
|
||||||
let (background, text, icon) = color(&cosmic.background(theme.transparent).component);
|
let (background, text, icon) = color(&cosmic.background.component);
|
||||||
|
|
||||||
if selected {
|
if selected {
|
||||||
appearance.background = Some(Background::Color(
|
appearance.background =
|
||||||
cosmic.primary(theme.transparent).component.hover.into(),
|
Some(Background::Color(cosmic.primary.component.hover.into()));
|
||||||
));
|
|
||||||
appearance.icon_color = Some(cosmic.accent.base.into());
|
appearance.icon_color = Some(cosmic.accent.base.into());
|
||||||
appearance.text_color = Some(cosmic.accent_text_color().into());
|
appearance.text_color = Some(cosmic.accent_text_color().into());
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -165,7 +164,7 @@ pub fn appearance(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button::MenuItem => {
|
Button::MenuItem => {
|
||||||
let (background, text, icon) = color(&cosmic.background(theme.transparent).component);
|
let (background, text, icon) = color(&cosmic.background.component);
|
||||||
appearance.background = Some(Background::Color(background));
|
appearance.background = Some(Background::Color(background));
|
||||||
appearance.icon_color = icon;
|
appearance.icon_color = icon;
|
||||||
appearance.text_color = text;
|
appearance.text_color = text;
|
||||||
|
|
@ -198,7 +197,7 @@ impl Catalog for crate::Theme {
|
||||||
return active(focused, self);
|
return active(focused, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
appearance(self, focused, selected, false, style, move |component| {
|
let mut s = appearance(self, focused, selected, false, style, move |component| {
|
||||||
let text_color = if matches!(
|
let text_color = if matches!(
|
||||||
style,
|
style,
|
||||||
Button::Icon | Button::IconVertical | Button::HeaderBar
|
Button::Icon | Button::IconVertical | Button::HeaderBar
|
||||||
|
|
@ -210,7 +209,15 @@ impl Catalog for crate::Theme {
|
||||||
};
|
};
|
||||||
|
|
||||||
(component.base.into(), text_color, text_color)
|
(component.base.into(), text_color, text_color)
|
||||||
})
|
});
|
||||||
|
|
||||||
|
if let Button::ListItem(_) = style {
|
||||||
|
if !selected {
|
||||||
|
s.background = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn disabled(&self, style: &Self::Class) -> Style {
|
fn disabled(&self, style: &Self::Class) -> Style {
|
||||||
|
|
@ -238,7 +245,7 @@ impl Catalog for crate::Theme {
|
||||||
return hovered(focused, self);
|
return hovered(focused, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
appearance(
|
let mut s = appearance(
|
||||||
self,
|
self,
|
||||||
focused || matches!(style, Button::Image),
|
focused || matches!(style, Button::Image),
|
||||||
selected,
|
selected,
|
||||||
|
|
@ -257,7 +264,15 @@ impl Catalog for crate::Theme {
|
||||||
|
|
||||||
(component.hover.into(), text_color, text_color)
|
(component.hover.into(), text_color, text_color)
|
||||||
},
|
},
|
||||||
)
|
);
|
||||||
|
|
||||||
|
if let Button::ListItem(_) = style {
|
||||||
|
if !selected {
|
||||||
|
s.background = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pressed(&self, focused: bool, selected: bool, style: &Self::Class) -> Style {
|
fn pressed(&self, focused: bool, selected: bool, style: &Self::Class) -> Style {
|
||||||
|
|
@ -281,6 +296,6 @@ impl Catalog for crate::Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selection_background(&self) -> Background {
|
fn selection_background(&self) -> Background {
|
||||||
Background::Color(self.cosmic().primary(self.transparent).base.into())
|
Background::Color(self.cosmic().primary.base.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,28 +13,18 @@ impl dropdown::menu::StyleSheet for Theme {
|
||||||
|
|
||||||
dropdown::menu::Appearance {
|
dropdown::menu::Appearance {
|
||||||
text_color: cosmic.on_bg_color().into(),
|
text_color: cosmic.on_bg_color().into(),
|
||||||
background: Background::Color(
|
background: Background::Color(cosmic.background.component.base.into()),
|
||||||
cosmic.background(self.transparent).component.base.into(),
|
|
||||||
),
|
|
||||||
border_width: 0.0,
|
border_width: 0.0,
|
||||||
border_radius: cosmic.corner_radii.radius_m.into(),
|
border_radius: cosmic.corner_radii.radius_m.into(),
|
||||||
border_color: Color::TRANSPARENT,
|
border_color: Color::TRANSPARENT,
|
||||||
|
|
||||||
hovered_text_color: cosmic.on_bg_color().into(),
|
hovered_text_color: cosmic.on_bg_color().into(),
|
||||||
hovered_background: Background::Color(
|
hovered_background: Background::Color(cosmic.primary.component.hover.into()),
|
||||||
cosmic.primary(self.transparent).component.hover.into(),
|
|
||||||
),
|
|
||||||
|
|
||||||
selected_text_color: cosmic.accent_text_color().into(),
|
selected_text_color: cosmic.accent_text_color().into(),
|
||||||
selected_background: Background::Color(
|
selected_background: Background::Color(cosmic.primary.component.hover.into()),
|
||||||
cosmic.primary(self.transparent).component.hover.into(),
|
|
||||||
),
|
|
||||||
|
|
||||||
description_color: cosmic
|
description_color: cosmic.primary.component.on_disabled.into(),
|
||||||
.primary(self.transparent)
|
|
||||||
.component
|
|
||||||
.on_disabled
|
|
||||||
.into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ pub mod application {
|
||||||
iced::theme::Style {
|
iced::theme::Style {
|
||||||
background_color: cosmic.bg_color().into(),
|
background_color: cosmic.bg_color().into(),
|
||||||
text_color: cosmic.on_bg_color().into(),
|
text_color: cosmic.on_bg_color().into(),
|
||||||
icon_color: cosmic.bg_color().into(),
|
icon_color: cosmic.on_bg_color().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -228,11 +228,11 @@ impl iced_checkbox::Catalog for Theme {
|
||||||
},
|
},
|
||||||
Checkbox::Secondary => iced_checkbox::Style {
|
Checkbox::Secondary => iced_checkbox::Style {
|
||||||
background: Background::Color(if is_checked {
|
background: Background::Color(if is_checked {
|
||||||
cosmic.background(self.transparent).component.base.into()
|
cosmic.background.component.base.into()
|
||||||
} else {
|
} else {
|
||||||
self.current_container().small_widget.into()
|
self.current_container().small_widget.into()
|
||||||
}),
|
}),
|
||||||
icon_color: cosmic.background(self.transparent).on.into(),
|
icon_color: cosmic.background.on.into(),
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: corners.radius_xs.into(),
|
radius: corners.radius_xs.into(),
|
||||||
width: if is_checked { 0.0 } else { 1.0 },
|
width: if is_checked { 0.0 } else { 1.0 },
|
||||||
|
|
@ -413,13 +413,11 @@ impl<'a> Container<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn background(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
|
pub fn background(theme: &cosmic_theme::Theme) -> iced_container::Style {
|
||||||
iced_container::Style {
|
iced_container::Style {
|
||||||
icon_color: Some(Color::from(theme.background(transparent).on)),
|
icon_color: Some(Color::from(theme.background.on)),
|
||||||
text_color: Some(Color::from(theme.background(transparent).on)),
|
text_color: Some(Color::from(theme.background.on)),
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(theme.background.base.into())),
|
||||||
theme.background(transparent).base.into(),
|
|
||||||
)),
|
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: theme.corner_radii.radius_s.into(),
|
radius: theme.corner_radii.radius_s.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -430,13 +428,11 @@ impl<'a> Container<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn primary(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
|
pub fn primary(theme: &cosmic_theme::Theme) -> iced_container::Style {
|
||||||
iced_container::Style {
|
iced_container::Style {
|
||||||
icon_color: Some(Color::from(theme.primary(transparent).on)),
|
icon_color: Some(Color::from(theme.primary.on)),
|
||||||
text_color: Some(Color::from(theme.primary(transparent).on)),
|
text_color: Some(Color::from(theme.primary.on)),
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(theme.primary.base.into())),
|
||||||
theme.primary(transparent).base.into(),
|
|
||||||
)),
|
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: theme.corner_radii.radius_s.into(),
|
radius: theme.corner_radii.radius_s.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -447,13 +443,11 @@ impl<'a> Container<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn secondary(theme: &cosmic_theme::Theme, transparent: bool) -> iced_container::Style {
|
pub fn secondary(theme: &cosmic_theme::Theme) -> iced_container::Style {
|
||||||
iced_container::Style {
|
iced_container::Style {
|
||||||
icon_color: Some(Color::from(theme.secondary(transparent).on)),
|
icon_color: Some(Color::from(theme.secondary.on)),
|
||||||
text_color: Some(Color::from(theme.secondary(transparent).on)),
|
text_color: Some(Color::from(theme.secondary.on)),
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(theme.secondary.base.into())),
|
||||||
theme.secondary(transparent).base.into(),
|
|
||||||
)),
|
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: theme.corner_radii.radius_s.into(),
|
radius: theme.corner_radii.radius_s.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|
@ -484,30 +478,14 @@ impl iced_container::Catalog for Theme {
|
||||||
let window_corner_radius = cosmic.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
|
let window_corner_radius = cosmic.radius_s().map(|x| if x < 4.0 { x } else { x + 4.0 });
|
||||||
|
|
||||||
match class {
|
match class {
|
||||||
Container::Transparent => {
|
Container::Transparent => iced_container::Style::default(),
|
||||||
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::Custom(f) => f(self),
|
||||||
|
|
||||||
Container::WindowBackground => iced_container::Style {
|
Container::WindowBackground => iced_container::Style {
|
||||||
icon_color: Some(Color::from(cosmic.background(self.transparent).on)),
|
icon_color: Some(Color::from(cosmic.background.on)),
|
||||||
text_color: Some(Color::from(cosmic.background(self.transparent).on)),
|
text_color: Some(Color::from(cosmic.background.on)),
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(cosmic.background.base.into())),
|
||||||
cosmic.background(self.transparent).base.into(),
|
|
||||||
)),
|
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: [
|
radius: [
|
||||||
cosmic.corner_radii.radius_0[0],
|
cosmic.corner_radii.radius_0[0],
|
||||||
|
|
@ -545,13 +523,12 @@ impl iced_container::Catalog for Theme {
|
||||||
let (icon_color, text_color) = if *focused {
|
let (icon_color, text_color) = if *focused {
|
||||||
(
|
(
|
||||||
Color::from(cosmic.accent_text_color()),
|
Color::from(cosmic.accent_text_color()),
|
||||||
Color::from(cosmic.background(self.transparent).on),
|
Color::from(cosmic.background.on),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
use crate::ext::ColorExt;
|
use crate::ext::ColorExt;
|
||||||
let unfocused_color =
|
let unfocused_color = Color::from(cosmic.background.component.on)
|
||||||
Color::from(cosmic.background(self.transparent).component.on)
|
.blend_alpha(cosmic.background.base.into(), 0.5);
|
||||||
.blend_alpha(cosmic.background(self.transparent).base.into(), 0.5);
|
|
||||||
(unfocused_color, unfocused_color)
|
(unfocused_color, unfocused_color)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -561,9 +538,7 @@ impl iced_container::Catalog for Theme {
|
||||||
background: if *transparent {
|
background: if *transparent {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(iced::Background::Color(
|
Some(iced::Background::Color(cosmic.background.base.into()))
|
||||||
cosmic.background(self.transparent).base.into(),
|
|
||||||
))
|
|
||||||
},
|
},
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: [
|
radius: [
|
||||||
|
|
@ -589,23 +564,20 @@ impl iced_container::Catalog for Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
Container::ContextDrawer => {
|
Container::ContextDrawer => {
|
||||||
let mut a = Container::primary(cosmic, self.transparent);
|
let mut a = Container::primary(cosmic);
|
||||||
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 {
|
if cosmic.is_high_contrast {
|
||||||
a.border.width = 1.;
|
a.border.width = 1.;
|
||||||
a.border.color = cosmic.primary(self.transparent).divider.into();
|
a.border.color = cosmic.primary.divider.into();
|
||||||
}
|
}
|
||||||
a
|
a
|
||||||
}
|
}
|
||||||
|
|
||||||
Container::Background => Container::background(cosmic, self.transparent),
|
Container::Background => Container::background(cosmic),
|
||||||
|
|
||||||
Container::Primary => Container::primary(cosmic, self.transparent),
|
Container::Primary => Container::primary(cosmic),
|
||||||
|
|
||||||
Container::Secondary => Container::secondary(cosmic, self.transparent),
|
Container::Secondary => Container::secondary(cosmic),
|
||||||
|
|
||||||
Container::Dropdown => iced_container::Style {
|
Container::Dropdown => iced_container::Style {
|
||||||
icon_color: None,
|
icon_color: None,
|
||||||
|
|
@ -637,14 +609,10 @@ impl iced_container::Catalog for Theme {
|
||||||
|
|
||||||
match self.layer {
|
match self.layer {
|
||||||
cosmic_theme::Layer::Background => iced_container::Style {
|
cosmic_theme::Layer::Background => iced_container::Style {
|
||||||
icon_color: Some(Color::from(
|
icon_color: Some(Color::from(cosmic.background.component.on)),
|
||||||
cosmic.background(self.transparent).component.on,
|
text_color: Some(Color::from(cosmic.background.component.on)),
|
||||||
)),
|
|
||||||
text_color: Some(Color::from(
|
|
||||||
cosmic.background(self.transparent).component.on,
|
|
||||||
)),
|
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(
|
||||||
cosmic.background(self.transparent).component.base.into(),
|
cosmic.background.component.base.into(),
|
||||||
)),
|
)),
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: cosmic.corner_radii.radius_s.into(),
|
radius: cosmic.corner_radii.radius_s.into(),
|
||||||
|
|
@ -654,14 +622,10 @@ impl iced_container::Catalog for Theme {
|
||||||
snap: true,
|
snap: true,
|
||||||
},
|
},
|
||||||
cosmic_theme::Layer::Primary => iced_container::Style {
|
cosmic_theme::Layer::Primary => iced_container::Style {
|
||||||
icon_color: Some(Color::from(
|
icon_color: Some(Color::from(cosmic.primary.component.on)),
|
||||||
cosmic.primary(self.transparent).component.on,
|
text_color: Some(Color::from(cosmic.primary.component.on)),
|
||||||
)),
|
|
||||||
text_color: Some(Color::from(
|
|
||||||
cosmic.primary(self.transparent).component.on,
|
|
||||||
)),
|
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(
|
||||||
cosmic.primary(self.transparent).component.base.into(),
|
cosmic.primary.component.base.into(),
|
||||||
)),
|
)),
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: cosmic.corner_radii.radius_s.into(),
|
radius: cosmic.corner_radii.radius_s.into(),
|
||||||
|
|
@ -671,14 +635,10 @@ impl iced_container::Catalog for Theme {
|
||||||
snap: true,
|
snap: true,
|
||||||
},
|
},
|
||||||
cosmic_theme::Layer::Secondary => iced_container::Style {
|
cosmic_theme::Layer::Secondary => iced_container::Style {
|
||||||
icon_color: Some(Color::from(
|
icon_color: Some(Color::from(cosmic.secondary.component.on)),
|
||||||
cosmic.secondary(self.transparent).component.on,
|
text_color: Some(Color::from(cosmic.secondary.component.on)),
|
||||||
)),
|
|
||||||
text_color: Some(Color::from(
|
|
||||||
cosmic.secondary(self.transparent).component.on,
|
|
||||||
)),
|
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(
|
||||||
cosmic.secondary(self.transparent).component.base.into(),
|
cosmic.secondary.component.base.into(),
|
||||||
)),
|
)),
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: cosmic.corner_radii.radius_s.into(),
|
radius: cosmic.corner_radii.radius_s.into(),
|
||||||
|
|
@ -691,13 +651,11 @@ impl iced_container::Catalog for Theme {
|
||||||
}
|
}
|
||||||
|
|
||||||
Container::Dialog => iced_container::Style {
|
Container::Dialog => iced_container::Style {
|
||||||
icon_color: Some(Color::from(cosmic.primary(self.transparent).on)),
|
icon_color: Some(Color::from(cosmic.primary.on)),
|
||||||
text_color: Some(Color::from(cosmic.primary(self.transparent).on)),
|
text_color: Some(Color::from(cosmic.primary.on)),
|
||||||
background: Some(iced::Background::Color(
|
background: Some(iced::Background::Color(cosmic.primary.base.into())),
|
||||||
cosmic.primary(self.transparent).base.into(),
|
|
||||||
)),
|
|
||||||
border: Border {
|
border: Border {
|
||||||
color: cosmic.primary(self.transparent).divider.into(),
|
color: cosmic.primary.divider.into(),
|
||||||
width: 1.0,
|
width: 1.0,
|
||||||
radius: cosmic.corner_radii.radius_m.into(),
|
radius: cosmic.corner_radii.radius_m.into(),
|
||||||
},
|
},
|
||||||
|
|
@ -839,15 +797,13 @@ impl menu::Catalog for Theme {
|
||||||
|
|
||||||
menu::Style {
|
menu::Style {
|
||||||
text_color: cosmic.on_bg_color().into(),
|
text_color: cosmic.on_bg_color().into(),
|
||||||
background: Background::Color(cosmic.background(self.transparent).base.into()),
|
background: Background::Color(cosmic.background.base.into()),
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: cosmic.corner_radii.radius_m.into(),
|
radius: cosmic.corner_radii.radius_m.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
},
|
},
|
||||||
selected_text_color: cosmic.accent_text_color().into(),
|
selected_text_color: cosmic.accent_text_color().into(),
|
||||||
selected_background: Background::Color(
|
selected_background: Background::Color(cosmic.background.component.hover.into()),
|
||||||
cosmic.background(self.transparent).component.hover.into(),
|
|
||||||
),
|
|
||||||
shadow: Default::default(),
|
shadow: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -885,7 +841,7 @@ impl pick_list::Catalog for Theme {
|
||||||
match status {
|
match status {
|
||||||
pick_list::Status::Active => appearance,
|
pick_list::Status::Active => appearance,
|
||||||
pick_list::Status::Hovered => pick_list::Style {
|
pick_list::Status::Hovered => pick_list::Style {
|
||||||
background: Background::Color(cosmic.background(self.transparent).base.into()),
|
background: Background::Color(cosmic.background.base.into()),
|
||||||
..appearance
|
..appearance
|
||||||
},
|
},
|
||||||
pick_list::Status::Opened { is_hovered: _ } => appearance,
|
pick_list::Status::Opened { is_hovered: _ } => appearance,
|
||||||
|
|
@ -1081,10 +1037,7 @@ impl progress_bar::Catalog for Theme {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(theme.accent.base, theme.background.divider)
|
||||||
theme.accent.base,
|
|
||||||
theme.background(self.transparent).divider,
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let border = Border {
|
let border = Border {
|
||||||
radius: theme.corner_radii.radius_xl.into(),
|
radius: theme.corner_radii.radius_xl.into(),
|
||||||
|
|
@ -1557,7 +1510,7 @@ impl iced_widget::text_editor::Catalog for Theme {
|
||||||
let selection = cosmic.accent.base.into();
|
let selection = cosmic.accent.base.into();
|
||||||
let value = cosmic.palette.neutral_9.into();
|
let value = cosmic.palette.neutral_9.into();
|
||||||
let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into();
|
let placeholder = cosmic.palette.neutral_9.with_alpha(0.7).into();
|
||||||
let icon: Color = cosmic.background(self.transparent).on.into();
|
let icon: Color = cosmic.background.on.into();
|
||||||
// TODO do we need to add icon color back?
|
// TODO do we need to add icon color back?
|
||||||
|
|
||||||
match status {
|
match status {
|
||||||
|
|
|
||||||
|
|
@ -64,13 +64,11 @@ impl StyleSheet for Theme {
|
||||||
|
|
||||||
fn appearance(&self, style: &Self::Style) -> Appearance {
|
fn appearance(&self, style: &Self::Style) -> Appearance {
|
||||||
let cosmic = self.cosmic();
|
let cosmic = self.cosmic();
|
||||||
let component = &cosmic.background(self.transparent).component;
|
let component = &cosmic.background.component;
|
||||||
let mut bg = component.base;
|
|
||||||
bg.alpha = (bg.alpha + if cosmic.is_dark { 0.6 } else { 0.5 }).min(1.);
|
|
||||||
|
|
||||||
match style {
|
match style {
|
||||||
MenuBarStyle::Default => Appearance {
|
MenuBarStyle::Default => Appearance {
|
||||||
background: bg.into(),
|
background: component.base.into(),
|
||||||
border_width: 1.0,
|
border_width: 1.0,
|
||||||
bar_border_radius: cosmic.corner_radii.radius_xl,
|
bar_border_radius: cosmic.corner_radii.radius_xl,
|
||||||
menu_border_radius: cosmic.corner_radii.radius_s.map(|x| x + 2.0),
|
menu_border_radius: cosmic.corner_radii.radius_s.map(|x| x + 2.0),
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
Apply, Element, fl,
|
Apply, Element, fl,
|
||||||
iced::{Alignment, Length},
|
iced::{Alignment, Length},
|
||||||
widget::{self, space},
|
widget::{self, list},
|
||||||
};
|
};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, derive_setters::Setters)]
|
#[derive(Debug, Default, Clone, derive_setters::Setters)]
|
||||||
#[setters(into, strip_option)]
|
#[setters(into, strip_option)]
|
||||||
|
|
@ -104,19 +105,23 @@ pub fn about<'a, Message: Clone + 'static>(
|
||||||
space_xxs, space_m, ..
|
space_xxs, space_m, ..
|
||||||
} = crate::theme::spacing();
|
} = crate::theme::spacing();
|
||||||
|
|
||||||
let section_button = |name: &'a str, url: &'a str| -> Element<'a, Message> {
|
let svg_accent = Rc::new(|theme: &crate::Theme| widget::svg::Style {
|
||||||
widget::row::with_capacity(3)
|
color: Some(theme.cosmic().accent_text_color().into()),
|
||||||
.push(widget::text(name))
|
});
|
||||||
.push(space::horizontal())
|
|
||||||
|
let section_button = |name: &'a str, url: &'a str| -> list::ListButton<'a, Message> {
|
||||||
|
widget::row::with_capacity(2)
|
||||||
|
.push(widget::text::body(name).width(Length::Fill))
|
||||||
.push_maybe(
|
.push_maybe(
|
||||||
(!url.is_empty()).then_some(crate::widget::icon::from_name("link-symbolic").icon()),
|
(!url.is_empty()).then_some(
|
||||||
|
widget::icon::from_name("link-symbolic")
|
||||||
|
.icon()
|
||||||
|
.class(crate::theme::Svg::Custom(svg_accent.clone())),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.align_y(Alignment::Center)
|
.align_y(Alignment::Center)
|
||||||
.apply(widget::button::custom)
|
.apply(list::button)
|
||||||
.class(crate::theme::Button::Link)
|
|
||||||
.on_press(on_url_press(url))
|
.on_press(on_url_press(url))
|
||||||
.width(Length::Fill)
|
|
||||||
.into()
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let section = |list: &'a Vec<(String, String)>, title: String| {
|
let section = |list: &'a Vec<(String, String)>, title: String| {
|
||||||
|
|
|
||||||
|
|
@ -31,26 +31,16 @@ impl crate::widget::card::style::Catalog for crate::Theme {
|
||||||
|
|
||||||
match self.layer {
|
match self.layer {
|
||||||
cosmic_theme::Layer::Background => crate::widget::card::style::Style {
|
cosmic_theme::Layer::Background => crate::widget::card::style::Style {
|
||||||
card_1: Background::Color(
|
card_1: Background::Color(cosmic.background.component.hover.into()),
|
||||||
cosmic.background(self.transparent).component.hover.into(),
|
card_2: Background::Color(cosmic.background.component.pressed.into()),
|
||||||
),
|
|
||||||
card_2: Background::Color(
|
|
||||||
cosmic.background(self.transparent).component.pressed.into(),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
cosmic_theme::Layer::Primary => crate::widget::card::style::Style {
|
cosmic_theme::Layer::Primary => crate::widget::card::style::Style {
|
||||||
card_1: Background::Color(cosmic.primary(self.transparent).component.hover.into()),
|
card_1: Background::Color(cosmic.primary.component.hover.into()),
|
||||||
card_2: Background::Color(
|
card_2: Background::Color(cosmic.primary.component.pressed.into()),
|
||||||
cosmic.primary(self.transparent).component.pressed.into(),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
cosmic_theme::Layer::Secondary => crate::widget::card::style::Style {
|
cosmic_theme::Layer::Secondary => crate::widget::card::style::Style {
|
||||||
card_1: Background::Color(
|
card_1: Background::Color(cosmic.secondary.component.hover.into()),
|
||||||
cosmic.secondary(self.transparent).component.hover.into(),
|
card_2: Background::Color(cosmic.secondary.component.pressed.into()),
|
||||||
),
|
|
||||||
card_2: Background::Color(
|
|
||||||
cosmic.secondary(self.transparent).component.pressed.into(),
|
|
||||||
),
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,10 +14,10 @@ use iced_core::image::Renderer as ImageRenderer;
|
||||||
use iced_core::mouse::Cursor;
|
use iced_core::mouse::Cursor;
|
||||||
use iced_core::widget::{Tree, tree};
|
use iced_core::widget::{Tree, tree};
|
||||||
use iced_core::{
|
use iced_core::{
|
||||||
Clipboard, ContentFit, Element, Event, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
|
Clipboard, ContentFit, Element, Event, Layout, Length, Rectangle, Rotation, Shell, Size,
|
||||||
event, layout, renderer, window,
|
Widget, event, layout, renderer, window,
|
||||||
};
|
};
|
||||||
use iced_widget::image::{self, Handle};
|
use iced_widget::image::{self, FilterMethod, Handle};
|
||||||
use image_rs::AnimationDecoder;
|
use image_rs::AnimationDecoder;
|
||||||
use image_rs::codecs::gif::GifDecoder;
|
use image_rs::codecs::gif::GifDecoder;
|
||||||
use image_rs::codecs::png::PngDecoder;
|
use image_rs::codecs::png::PngDecoder;
|
||||||
|
|
@ -146,7 +146,7 @@ impl Frames {
|
||||||
|
|
||||||
match image_type {
|
match image_type {
|
||||||
ImageType::Gif => Self::from_decoder(GifDecoder::new(io::Cursor::new(bytes))?),
|
ImageType::Gif => Self::from_decoder(GifDecoder::new(io::Cursor::new(bytes))?),
|
||||||
ImageType::Apng => Self::from_decoder(PngDecoder::new(io::Cursor::new(bytes))?.apng()),
|
ImageType::Apng => Self::from_decoder(PngDecoder::new(io::Cursor::new(bytes))?.apng()?),
|
||||||
ImageType::WebP => Self::from_decoder(WebPDecoder::new(io::Cursor::new(bytes))?),
|
ImageType::WebP => Self::from_decoder(WebPDecoder::new(io::Cursor::new(bytes))?),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -168,10 +168,10 @@ impl Frames {
|
||||||
let first = frames.first().cloned().unwrap();
|
let first = frames.first().cloned().unwrap();
|
||||||
let total_bytes = frames
|
let total_bytes = frames
|
||||||
.iter()
|
.iter()
|
||||||
.map(|f| match f.handle.data() {
|
.map(|f| match &f.handle {
|
||||||
iced_core::image::Handle::Path(..) => 0,
|
Handle::Path(..) => 0,
|
||||||
iced_core::image::Handle::Bytes(_, b) => b.len(),
|
Handle::Bytes(_, b) => b.len(),
|
||||||
iced_core::image::Handle::Rgba { pixels, .. } => pixels.len(),
|
Handle::Rgba { pixels, .. } => pixels.len(),
|
||||||
})
|
})
|
||||||
.sum::<usize>()
|
.sum::<usize>()
|
||||||
.try_into()
|
.try_into()
|
||||||
|
|
@ -324,7 +324,11 @@ where
|
||||||
&self.frames.first.handle,
|
&self.frames.first.handle,
|
||||||
self.width,
|
self.width,
|
||||||
self.height,
|
self.height,
|
||||||
|
None,
|
||||||
self.content_fit,
|
self.content_fit,
|
||||||
|
Rotation::default(),
|
||||||
|
false,
|
||||||
|
[0.0; 4],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -371,37 +375,18 @@ where
|
||||||
) {
|
) {
|
||||||
let state = tree.state.downcast_ref::<State>();
|
let state = tree.state.downcast_ref::<State>();
|
||||||
|
|
||||||
// Pulled from iced_native::widget::<Image as Widget>::draw
|
iced_widget::image::draw(
|
||||||
//
|
renderer,
|
||||||
// TODO: export iced_native::widget::image::draw as standalone function
|
layout,
|
||||||
{
|
&state.current.frame.handle,
|
||||||
let Size { width, height } = renderer.dimensions(&state.current.frame.handle);
|
None,
|
||||||
let image_size = Size::new(width as f32, height as f32);
|
iced_core::border::Radius::default(),
|
||||||
|
self.content_fit,
|
||||||
let bounds = layout.bounds();
|
FilterMethod::default(),
|
||||||
let adjusted_fit = self.content_fit.fit(image_size, bounds.size());
|
Rotation::default(),
|
||||||
|
1.0,
|
||||||
let render = |renderer: &mut Renderer| {
|
1.0,
|
||||||
let offset = Vector::new(
|
);
|
||||||
(bounds.width - adjusted_fit.width).max(0.0) / 2.0,
|
|
||||||
(bounds.height - adjusted_fit.height).max(0.0) / 2.0,
|
|
||||||
);
|
|
||||||
|
|
||||||
let drawing_bounds = Rectangle {
|
|
||||||
width: adjusted_fit.width,
|
|
||||||
height: adjusted_fit.height,
|
|
||||||
..bounds
|
|
||||||
};
|
|
||||||
|
|
||||||
renderer.draw(state.current.frame.handle.clone(), drawing_bounds + offset);
|
|
||||||
};
|
|
||||||
|
|
||||||
if adjusted_fit.width > bounds.width || adjusted_fit.height > bounds.height {
|
|
||||||
renderer.with_layer(bounds, render);
|
|
||||||
} else {
|
|
||||||
render(renderer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,12 @@
|
||||||
//! Embedded icons for platforms which do not support icon themes yet.
|
//! Embedded icons for platforms which do not support icon themes yet.
|
||||||
|
|
||||||
/// Icon bundling is not enabled on unix platforms.
|
/// Icon bundling is not enabled on unix platforms.
|
||||||
#[cfg(unix)]
|
#[cfg(all(unix, not(target_os = "macos")))]
|
||||||
pub fn get(icon_name: &str) -> Option<super::Data> {
|
pub fn get(icon_name: &str) -> Option<super::Data> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(any(not(unix), target_os = "macos"))]
|
||||||
/// Get a bundled icon on non-unix platforms.
|
/// Get a bundled icon on non-unix platforms.
|
||||||
pub fn get(icon_name: &str) -> Option<super::Data> {
|
pub fn get(icon_name: &str) -> Option<super::Data> {
|
||||||
ICONS
|
ICONS
|
||||||
|
|
@ -17,5 +17,5 @@ pub fn get(icon_name: &str) -> Option<super::Data> {
|
||||||
.map(|bytes| super::Data::Svg(crate::iced::widget::svg::Handle::from_memory(*bytes)))
|
.map(|bytes| super::Data::Svg(crate::iced::widget::svg::Handle::from_memory(*bytes)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(unix))]
|
#[cfg(any(not(unix), target_os = "macos"))]
|
||||||
include!(concat!(env!("OUT_DIR"), "/bundled_icons.rs"));
|
include!(concat!(env!("OUT_DIR"), "/bundled_icons.rs"));
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ impl Named {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(windows))]
|
#[cfg(all(unix, not(target_os = "macos")))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn path(self) -> Option<PathBuf> {
|
pub fn path(self) -> Option<PathBuf> {
|
||||||
let name = &*self.name;
|
let name = &*self.name;
|
||||||
|
|
@ -107,7 +107,7 @@ impl Named {
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(any(not(unix), target_os = "macos"))]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn path(self) -> Option<PathBuf> {
|
pub fn path(self) -> Option<PathBuf> {
|
||||||
//TODO: implement icon lookup for Windows
|
//TODO: implement icon lookup for Windows
|
||||||
|
|
|
||||||
|
|
@ -1,128 +0,0 @@
|
||||||
// Copyright 2022 System76 <info@system76.com>
|
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
|
||||||
|
|
||||||
use iced_core::Padding;
|
|
||||||
use iced_widget::container::Catalog;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
Apply, Element, theme,
|
|
||||||
widget::{container, divider, space::vertical},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn list_column<'a, Message: 'static>() -> ListColumn<'a, Message> {
|
|
||||||
ListColumn::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub struct ListColumn<'a, Message> {
|
|
||||||
spacing: u16,
|
|
||||||
padding: Padding,
|
|
||||||
list_item_padding: Padding,
|
|
||||||
divider_padding: u16,
|
|
||||||
style: theme::Container<'a>,
|
|
||||||
children: Vec<Element<'a, Message>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Message: 'static> Default for ListColumn<'_, Message> {
|
|
||||||
fn default() -> Self {
|
|
||||||
let cosmic_theme::Spacing {
|
|
||||||
space_xxs, space_m, ..
|
|
||||||
} = theme::spacing();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
spacing: 0,
|
|
||||||
padding: Padding::from(0),
|
|
||||||
divider_padding: 16,
|
|
||||||
list_item_padding: [space_xxs, space_m].into(),
|
|
||||||
style: theme::Container::List,
|
|
||||||
children: Vec::with_capacity(4),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message: 'static> ListColumn<'a, Message> {
|
|
||||||
#[inline]
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self::default()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::should_implement_trait)]
|
|
||||||
pub fn add(self, item: impl Into<Element<'a, Message>>) -> Self {
|
|
||||||
#[inline(never)]
|
|
||||||
fn inner<'a, Message: 'static>(
|
|
||||||
mut this: ListColumn<'a, Message>,
|
|
||||||
item: Element<'a, Message>,
|
|
||||||
) -> ListColumn<'a, Message> {
|
|
||||||
if !this.children.is_empty() {
|
|
||||||
this.children.push(
|
|
||||||
container(divider::horizontal::default())
|
|
||||||
.padding([0, this.divider_padding])
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure a minimum height of 32.
|
|
||||||
let list_item = crate::widget::row![
|
|
||||||
container(item).align_y(iced::Alignment::Center),
|
|
||||||
vertical().height(iced::Length::Fixed(32.))
|
|
||||||
]
|
|
||||||
.padding(this.list_item_padding)
|
|
||||||
.align_y(iced::Alignment::Center);
|
|
||||||
|
|
||||||
this.children.push(list_item.into());
|
|
||||||
this
|
|
||||||
}
|
|
||||||
|
|
||||||
inner(self, item.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn spacing(mut self, spacing: u16) -> Self {
|
|
||||||
self.spacing = spacing;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the style variant of this [`Circular`].
|
|
||||||
#[inline]
|
|
||||||
pub fn style(mut self, style: <crate::Theme as Catalog>::Class<'a>) -> Self {
|
|
||||||
self.style = style;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn padding(mut self, padding: impl Into<Padding>) -> Self {
|
|
||||||
self.padding = padding.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn divider_padding(mut self, padding: u16) -> Self {
|
|
||||||
self.divider_padding = padding;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn list_item_padding(mut self, padding: impl Into<Padding>) -> Self {
|
|
||||||
self.list_item_padding = padding.into();
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
pub fn into_element(self) -> Element<'a, Message> {
|
|
||||||
crate::widget::column::with_children(self.children)
|
|
||||||
.spacing(self.spacing)
|
|
||||||
.padding(self.padding)
|
|
||||||
.width(iced::Length::Fill)
|
|
||||||
.apply(container)
|
|
||||||
.padding([self.spacing, 0])
|
|
||||||
.class(self.style)
|
|
||||||
.width(iced::Length::Fill)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, Message: 'static> From<ListColumn<'a, Message>> for Element<'a, Message> {
|
|
||||||
fn from(column: ListColumn<'a, Message>) -> Self {
|
|
||||||
column.into_element()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
213
src/widget/list/list_column.rs
Normal file
213
src/widget/list/list_column.rs
Normal file
|
|
@ -0,0 +1,213 @@
|
||||||
|
// Copyright 2022 System76 <info@system76.com>
|
||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
|
use crate::widget::container::Catalog;
|
||||||
|
use crate::widget::{button, column, container, divider, row, space::vertical};
|
||||||
|
use crate::{Apply, Element, theme};
|
||||||
|
use iced::{Length, Padding};
|
||||||
|
|
||||||
|
/// A button list item for use in a [`ListColumn`].
|
||||||
|
pub struct ListButton<'a, Message> {
|
||||||
|
content: Element<'a, Message>,
|
||||||
|
on_press: Option<Message>,
|
||||||
|
selected: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a [`ListButton`] with the given content.
|
||||||
|
pub fn button<'a, Message>(content: impl Into<Element<'a, Message>>) -> ListButton<'a, Message> {
|
||||||
|
ListButton {
|
||||||
|
content: content.into(),
|
||||||
|
on_press: None,
|
||||||
|
selected: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message: 'static> ListButton<'a, Message> {
|
||||||
|
pub fn on_press(mut self, on_press: Message) -> Self {
|
||||||
|
self.on_press = Some(on_press);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn on_press_maybe(mut self, on_press: Option<Message>) -> Self {
|
||||||
|
self.on_press = on_press;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected(mut self, selected: bool) -> Self {
|
||||||
|
self.selected = selected;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ListItem<'a, Message> {
|
||||||
|
Element(Element<'a, Message>),
|
||||||
|
Button(ListButton<'a, Message>),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A trait for types that can be added to a [`ListColumn`].
|
||||||
|
pub trait IntoListItem<'a, Message> {
|
||||||
|
fn into_list_item(self) -> ListItem<'a, Message>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message, T> IntoListItem<'a, Message> for T
|
||||||
|
where
|
||||||
|
T: Into<Element<'a, Message>>,
|
||||||
|
{
|
||||||
|
fn into_list_item(self) -> ListItem<'a, Message> {
|
||||||
|
ListItem::Element(self.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message> IntoListItem<'a, Message> for ListButton<'a, Message> {
|
||||||
|
fn into_list_item(self) -> ListItem<'a, Message> {
|
||||||
|
ListItem::Button(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Snapshots the padding values at the moment an item is added
|
||||||
|
struct ListEntry<'a, Message> {
|
||||||
|
item: ListItem<'a, Message>,
|
||||||
|
item_padding: Padding,
|
||||||
|
divider_padding: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub struct ListColumn<'a, Message> {
|
||||||
|
list_item_padding: Padding,
|
||||||
|
divider_padding: u16,
|
||||||
|
style: theme::Container<'a>,
|
||||||
|
children: Vec<ListEntry<'a, Message>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn list_column<'a, Message: 'static>() -> ListColumn<'a, Message> {
|
||||||
|
ListColumn::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_capacity<'a, Message: 'static>(capacity: usize) -> ListColumn<'a, Message> {
|
||||||
|
let cosmic_theme::Spacing {
|
||||||
|
space_xxs, space_m, ..
|
||||||
|
} = theme::spacing();
|
||||||
|
|
||||||
|
ListColumn {
|
||||||
|
list_item_padding: [space_xxs, space_m].into(),
|
||||||
|
divider_padding: 0,
|
||||||
|
style: theme::Container::List,
|
||||||
|
children: Vec::with_capacity(capacity),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Message: 'static> Default for ListColumn<'_, Message> {
|
||||||
|
fn default() -> Self {
|
||||||
|
with_capacity(4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message: Clone + 'static> ListColumn<'a, Message> {
|
||||||
|
#[inline]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a [`ListItem`] to the [`ListColumn`].
|
||||||
|
#[allow(clippy::should_implement_trait)]
|
||||||
|
pub fn add(mut self, item: impl IntoListItem<'a, Message>) -> Self {
|
||||||
|
self.children.push(ListEntry {
|
||||||
|
item: item.into_list_item(),
|
||||||
|
item_padding: self.list_item_padding,
|
||||||
|
divider_padding: self.divider_padding,
|
||||||
|
});
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the style variant of this [`ListColumn`].
|
||||||
|
#[inline]
|
||||||
|
pub fn style(mut self, style: <crate::Theme as Catalog>::Class<'a>) -> Self {
|
||||||
|
self.style = style;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_item_padding(mut self, padding: impl Into<Padding>) -> Self {
|
||||||
|
self.list_item_padding = padding.into();
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn divider_padding(mut self, padding: u16) -> Self {
|
||||||
|
self.divider_padding = padding;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn into_element(self) -> Element<'a, Message> {
|
||||||
|
let count = self.children.len();
|
||||||
|
let last_index = count.saturating_sub(1);
|
||||||
|
let radius_s = theme::active().cosmic().radius_s();
|
||||||
|
let mut col = column::with_capacity((2 * count).saturating_sub(1));
|
||||||
|
|
||||||
|
// Ensure minimum height of 32
|
||||||
|
let content_row = |content| {
|
||||||
|
row![container(content), vertical().height(32)].align_y(iced::Alignment::Center)
|
||||||
|
};
|
||||||
|
|
||||||
|
for (
|
||||||
|
i,
|
||||||
|
ListEntry {
|
||||||
|
item,
|
||||||
|
item_padding,
|
||||||
|
divider_padding,
|
||||||
|
},
|
||||||
|
) in self.children.into_iter().enumerate()
|
||||||
|
{
|
||||||
|
if i > 0 {
|
||||||
|
col = col
|
||||||
|
.push(container(divider::horizontal::default()).padding([0, divider_padding]));
|
||||||
|
}
|
||||||
|
|
||||||
|
col = match item {
|
||||||
|
ListItem::Element(content) => col.push(
|
||||||
|
content_row(content)
|
||||||
|
.padding(item_padding)
|
||||||
|
.width(Length::Fill),
|
||||||
|
),
|
||||||
|
ListItem::Button(ListButton {
|
||||||
|
content,
|
||||||
|
on_press,
|
||||||
|
selected,
|
||||||
|
}) => col.push(
|
||||||
|
content_row(content)
|
||||||
|
.apply(button::custom)
|
||||||
|
.padding(item_padding)
|
||||||
|
.width(Length::Fill)
|
||||||
|
.on_press_maybe(on_press)
|
||||||
|
.selected(selected)
|
||||||
|
.class(theme::Button::ListItem(get_radius(
|
||||||
|
radius_s,
|
||||||
|
i == 0,
|
||||||
|
i == last_index,
|
||||||
|
))),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
col.width(Length::Fill)
|
||||||
|
.apply(container)
|
||||||
|
.class(self.style)
|
||||||
|
.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, Message: Clone + 'static> From<ListColumn<'a, Message>> for Element<'a, Message> {
|
||||||
|
fn from(column: ListColumn<'a, Message>) -> Self {
|
||||||
|
column.into_element()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_radius(radius: [f32; 4], first: bool, last: bool) -> [f32; 4] {
|
||||||
|
match (first, last) {
|
||||||
|
(true, true) => radius,
|
||||||
|
(true, false) => [radius[0], radius[1], 0.0, 0.0],
|
||||||
|
(false, true) => [0.0, 0.0, radius[2], radius[3]],
|
||||||
|
(false, false) => [0.0, 0.0, 0.0, 0.0],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// Copyright 2022 System76 <info@system76.com>
|
// Copyright 2022 System76 <info@system76.com>
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
pub mod column;
|
pub mod list_column;
|
||||||
|
|
||||||
pub use self::column::{ListColumn, list_column};
|
pub use self::list_column::{ListButton, ListColumn, button, list_column};
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ pub fn menu_items<
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_style(theme: &crate::Theme) -> TextStyle {
|
fn key_style(theme: &crate::Theme) -> TextStyle {
|
||||||
let mut color = theme.cosmic().background(theme.transparent).component.on;
|
let mut color = theme.cosmic().background.component.on;
|
||||||
color.alpha *= 0.75;
|
color.alpha *= 0.75;
|
||||||
TextStyle {
|
TextStyle {
|
||||||
color: Some(color.into()),
|
color: Some(color.into()),
|
||||||
|
|
|
||||||
|
|
@ -173,9 +173,7 @@ pub fn nav_bar_style(theme: &Theme) -> iced_widget::container::Style {
|
||||||
iced_widget::container::Style {
|
iced_widget::container::Style {
|
||||||
icon_color: Some(cosmic.on_bg_color().into()),
|
icon_color: Some(cosmic.on_bg_color().into()),
|
||||||
text_color: Some(cosmic.on_bg_color().into()),
|
text_color: Some(cosmic.on_bg_color().into()),
|
||||||
background: Some(Background::Color(
|
background: Some(Background::Color(cosmic.primary.base.into())),
|
||||||
cosmic.primary(theme.transparent).base.into(),
|
|
||||||
)),
|
|
||||||
border: Border {
|
border: Border {
|
||||||
width: 0.0,
|
width: 0.0,
|
||||||
color: Color::TRANSPARENT,
|
color: Color::TRANSPARENT,
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ use std::f32::consts::PI;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
const MIN_ANGLE: Radians = Radians(PI / 8.0);
|
const MIN_ANGLE: Radians = Radians(PI / 8.0);
|
||||||
const WRAP_ANGLE: Radians = Radians(2.0 * PI - PI / 4.0);
|
|
||||||
const BASE_ROTATION_SPEED: u32 = u32::MAX / 80;
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Circular<Theme>
|
pub struct Circular<Theme>
|
||||||
|
|
@ -83,6 +81,12 @@ where
|
||||||
self.progress = Some(progress.clamp(0.0, 1.0));
|
self.progress = Some(progress.clamp(0.0, 1.0));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn min_wrap_angle(&self, track_radius: f32) -> (f32, f32) {
|
||||||
|
let cap_angle = self.bar_height / track_radius;
|
||||||
|
let gap = MIN_ANGLE.0.max(cap_angle);
|
||||||
|
(gap - cap_angle, 2.0 * PI - gap * 2.0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Theme> Default for Circular<Theme>
|
impl<Theme> Default for Circular<Theme>
|
||||||
|
|
@ -122,7 +126,7 @@ impl Default for Animation {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Animation {
|
impl Animation {
|
||||||
fn next(&self, additional_rotation: u32, now: Instant) -> Self {
|
fn next(&self, additional_rotation: u32, wrap_angle: f32, now: Instant) -> Self {
|
||||||
match self {
|
match self {
|
||||||
Self::Expanding { rotation, .. } => Self::Contracting {
|
Self::Expanding { rotation, .. } => Self::Contracting {
|
||||||
start: now,
|
start: now,
|
||||||
|
|
@ -133,9 +137,9 @@ impl Animation {
|
||||||
Self::Contracting { rotation, .. } => Self::Expanding {
|
Self::Contracting { rotation, .. } => Self::Expanding {
|
||||||
start: now,
|
start: now,
|
||||||
progress: 0.0,
|
progress: 0.0,
|
||||||
rotation: rotation.wrapping_add(BASE_ROTATION_SPEED.wrapping_add(
|
rotation: rotation.wrapping_add(
|
||||||
(f64::from(WRAP_ANGLE / (2.0 * Radians::PI)) * f64::from(u32::MAX)) as u32,
|
(f64::from((wrap_angle) / (2.0 * PI)) * f64::from(u32::MAX)) as u32,
|
||||||
)),
|
),
|
||||||
last: now,
|
last: now,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -157,6 +161,7 @@ impl Animation {
|
||||||
&self,
|
&self,
|
||||||
cycle_duration: Duration,
|
cycle_duration: Duration,
|
||||||
rotation_duration: Duration,
|
rotation_duration: Duration,
|
||||||
|
wrap_angle: f32,
|
||||||
now: Instant,
|
now: Instant,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let elapsed = now.duration_since(self.start());
|
let elapsed = now.duration_since(self.start());
|
||||||
|
|
@ -165,7 +170,7 @@ impl Animation {
|
||||||
* (u32::MAX) as f32) as u32;
|
* (u32::MAX) as f32) as u32;
|
||||||
|
|
||||||
match elapsed {
|
match elapsed {
|
||||||
elapsed if elapsed > cycle_duration => self.next(additional_rotation, now),
|
elapsed if elapsed > cycle_duration => self.next(additional_rotation, wrap_angle, now),
|
||||||
_ => self.with_elapsed(cycle_duration, additional_rotation, elapsed, now),
|
_ => self.with_elapsed(cycle_duration, additional_rotation, elapsed, now),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -267,10 +272,13 @@ where
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if let Event::Window(window::Event::RedrawRequested(now)) = event {
|
if let Event::Window(window::Event::RedrawRequested(now)) = event {
|
||||||
state.animation =
|
let (_, wrap_angle) = self.min_wrap_angle(self.size / 2.0 - self.bar_height);
|
||||||
state
|
state.animation = state.animation.timed_transition(
|
||||||
.animation
|
self.cycle_duration,
|
||||||
.timed_transition(self.cycle_duration, self.rotation_duration, *now);
|
self.rotation_duration,
|
||||||
|
wrap_angle,
|
||||||
|
*now,
|
||||||
|
);
|
||||||
|
|
||||||
state.cache.clear();
|
state.cache.clear();
|
||||||
shell.request_redraw();
|
shell.request_redraw();
|
||||||
|
|
@ -380,22 +388,23 @@ where
|
||||||
} else {
|
} else {
|
||||||
let mut builder = canvas::path::Builder::new();
|
let mut builder = canvas::path::Builder::new();
|
||||||
|
|
||||||
let start = Radians(state.animation.rotation() * 2.0 * PI);
|
let start = state.animation.rotation() * 2.0 * PI;
|
||||||
|
let (min_angle, wrap_angle) = self.min_wrap_angle(track_radius);
|
||||||
let (start_angle, end_angle) = match state.animation {
|
let (start_angle, end_angle) = match state.animation {
|
||||||
Animation::Expanding { progress, .. } => (
|
Animation::Expanding { progress, .. } => (
|
||||||
start,
|
start,
|
||||||
start + MIN_ANGLE + WRAP_ANGLE * (smootherstep(progress)),
|
start + min_angle + wrap_angle * smootherstep(progress),
|
||||||
),
|
),
|
||||||
Animation::Contracting { progress, .. } => (
|
Animation::Contracting { progress, .. } => (
|
||||||
start + WRAP_ANGLE * (smootherstep(progress)),
|
start + wrap_angle * smootherstep(progress),
|
||||||
start + MIN_ANGLE + WRAP_ANGLE,
|
start + min_angle + wrap_angle,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
builder.arc(canvas::path::Arc {
|
builder.arc(canvas::path::Arc {
|
||||||
center: frame.center(),
|
center: frame.center(),
|
||||||
radius: track_radius,
|
radius: track_radius,
|
||||||
start_angle,
|
start_angle: Radians(start_angle),
|
||||||
end_angle,
|
end_angle: Radians(end_angle),
|
||||||
});
|
});
|
||||||
|
|
||||||
let bar_path = builder.build();
|
let bar_path = builder.build();
|
||||||
|
|
@ -410,23 +419,23 @@ where
|
||||||
let mut builder = canvas::path::Builder::new();
|
let mut builder = canvas::path::Builder::new();
|
||||||
|
|
||||||
// get center of end of arc for rounded cap
|
// get center of end of arc for rounded cap
|
||||||
let end_center = frame.center()
|
let end_center =
|
||||||
+ Vector::new(end_angle.0.cos(), end_angle.0.sin()) * track_radius;
|
frame.center() + Vector::new(end_angle.cos(), end_angle.sin()) * track_radius;
|
||||||
builder.arc(canvas::path::Arc {
|
builder.arc(canvas::path::Arc {
|
||||||
center: end_center,
|
center: end_center,
|
||||||
radius: self.bar_height / 2.0,
|
radius: self.bar_height / 2.0,
|
||||||
start_angle: Radians(end_angle.0),
|
start_angle: Radians(end_angle),
|
||||||
end_angle: Radians(end_angle.0 + PI),
|
end_angle: Radians(end_angle + PI),
|
||||||
});
|
});
|
||||||
|
|
||||||
// get center of start of arc for rounded cap
|
// get center of start of arc for rounded cap
|
||||||
let start_center = frame.center()
|
let start_center = frame.center()
|
||||||
+ Vector::new(start_angle.0.cos(), start_angle.0.sin()) * track_radius;
|
+ Vector::new(start_angle.cos(), start_angle.sin()) * track_radius;
|
||||||
builder.arc(canvas::path::Arc {
|
builder.arc(canvas::path::Arc {
|
||||||
center: start_center,
|
center: start_center,
|
||||||
radius: self.bar_height / 2.0,
|
radius: self.bar_height / 2.0,
|
||||||
start_angle: Radians(start_angle.0 - PI),
|
start_angle: Radians(start_angle - PI),
|
||||||
end_angle: Radians(start_angle.0),
|
end_angle: Radians(start_angle),
|
||||||
});
|
});
|
||||||
|
|
||||||
let cap_path = builder.build();
|
let cap_path = builder.build();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//! Create choices using radio buttons.
|
//! Create choices using radio buttons.
|
||||||
use crate::Theme;
|
use crate::{Theme, theme};
|
||||||
use iced::border;
|
use iced::border;
|
||||||
use iced_core::event::{self, Event};
|
use iced_core::event::{self, Event};
|
||||||
use iced_core::layout;
|
use iced_core::layout;
|
||||||
|
|
@ -92,7 +92,7 @@ where
|
||||||
{
|
{
|
||||||
is_selected: bool,
|
is_selected: bool,
|
||||||
on_click: Message,
|
on_click: Message,
|
||||||
label: Element<'a, Message, Theme, Renderer>,
|
label: Option<Element<'a, Message, Theme, Renderer>>,
|
||||||
width: Length,
|
width: Length,
|
||||||
size: f32,
|
size: f32,
|
||||||
spacing: f32,
|
spacing: f32,
|
||||||
|
|
@ -106,9 +106,6 @@ where
|
||||||
/// The default size of a [`Radio`] button.
|
/// The default size of a [`Radio`] button.
|
||||||
pub const DEFAULT_SIZE: f32 = 16.0;
|
pub const DEFAULT_SIZE: f32 = 16.0;
|
||||||
|
|
||||||
/// The default spacing of a [`Radio`] button.
|
|
||||||
pub const DEFAULT_SPACING: f32 = 8.0;
|
|
||||||
|
|
||||||
/// Creates a new [`Radio`] button.
|
/// Creates a new [`Radio`] button.
|
||||||
///
|
///
|
||||||
/// It expects:
|
/// It expects:
|
||||||
|
|
@ -126,10 +123,29 @@ where
|
||||||
Radio {
|
Radio {
|
||||||
is_selected: Some(value) == selected,
|
is_selected: Some(value) == selected,
|
||||||
on_click: f(value),
|
on_click: f(value),
|
||||||
label: label.into(),
|
label: Some(label.into()),
|
||||||
width: Length::Shrink,
|
width: Length::Shrink,
|
||||||
size: Self::DEFAULT_SIZE,
|
size: Self::DEFAULT_SIZE,
|
||||||
spacing: Self::DEFAULT_SPACING,
|
spacing: theme::spacing().space_xs as f32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`Radio`] button without a label.
|
||||||
|
///
|
||||||
|
/// This is intended for internal use with the settings item builder,
|
||||||
|
/// where the label comes from the settings item title instead.
|
||||||
|
pub(crate) fn new_no_label<V, F>(value: V, selected: Option<V>, f: F) -> Self
|
||||||
|
where
|
||||||
|
V: Eq + Copy,
|
||||||
|
F: FnOnce(V) -> Message,
|
||||||
|
{
|
||||||
|
Radio {
|
||||||
|
is_selected: Some(value) == selected,
|
||||||
|
on_click: f(value),
|
||||||
|
label: None,
|
||||||
|
width: Length::Shrink,
|
||||||
|
size: Self::DEFAULT_SIZE,
|
||||||
|
spacing: theme::spacing().space_xs as f32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,11 +177,17 @@ where
|
||||||
Renderer: iced_core::Renderer,
|
Renderer: iced_core::Renderer,
|
||||||
{
|
{
|
||||||
fn children(&self) -> Vec<Tree> {
|
fn children(&self) -> Vec<Tree> {
|
||||||
vec![Tree::new(&self.label)]
|
if let Some(label) = &self.label {
|
||||||
|
vec![Tree::new(label)]
|
||||||
|
} else {
|
||||||
|
vec![]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn diff(&mut self, tree: &mut Tree) {
|
fn diff(&mut self, tree: &mut Tree) {
|
||||||
tree.diff_children(std::slice::from_mut(&mut self.label));
|
if let Some(label) = &mut self.label {
|
||||||
|
tree.diff_children(std::slice::from_mut(label));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fn size(&self) -> Size<Length> {
|
fn size(&self) -> Size<Length> {
|
||||||
Size {
|
Size {
|
||||||
|
|
@ -180,16 +202,20 @@ where
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
limits: &layout::Limits,
|
limits: &layout::Limits,
|
||||||
) -> layout::Node {
|
) -> layout::Node {
|
||||||
layout::next_to_each_other(
|
if let Some(label) = &mut self.label {
|
||||||
&limits.width(self.width),
|
layout::next_to_each_other(
|
||||||
self.spacing,
|
&limits.width(self.width),
|
||||||
|_| layout::Node::new(Size::new(self.size, self.size)),
|
self.spacing,
|
||||||
|limits| {
|
|_| layout::Node::new(Size::new(self.size, self.size)),
|
||||||
self.label
|
|limits| {
|
||||||
.as_widget_mut()
|
label
|
||||||
.layout(&mut tree.children[0], renderer, limits)
|
.as_widget_mut()
|
||||||
},
|
.layout(&mut tree.children[0], renderer, limits)
|
||||||
)
|
},
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
layout::Node::new(Size::new(self.size, self.size))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn operate(
|
fn operate(
|
||||||
|
|
@ -199,12 +225,14 @@ where
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
operation: &mut dyn iced_core::widget::Operation<()>,
|
operation: &mut dyn iced_core::widget::Operation<()>,
|
||||||
) {
|
) {
|
||||||
self.label.as_widget_mut().operate(
|
if let Some(label) = &mut self.label {
|
||||||
&mut tree.children[0],
|
label.as_widget_mut().operate(
|
||||||
layout.children().nth(1).unwrap(),
|
&mut tree.children[0],
|
||||||
renderer,
|
layout.children().nth(1).unwrap(),
|
||||||
operation,
|
renderer,
|
||||||
);
|
operation,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update(
|
fn update(
|
||||||
|
|
@ -218,24 +246,25 @@ where
|
||||||
shell: &mut Shell<'_, Message>,
|
shell: &mut Shell<'_, Message>,
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
) {
|
) {
|
||||||
self.label.as_widget_mut().update(
|
if let Some(label) = &mut self.label {
|
||||||
&mut tree.children[0],
|
label.as_widget_mut().update(
|
||||||
event,
|
&mut tree.children[0],
|
||||||
layout.children().nth(1).unwrap(),
|
event,
|
||||||
cursor,
|
layout.children().nth(1).unwrap(),
|
||||||
renderer,
|
cursor,
|
||||||
clipboard,
|
renderer,
|
||||||
shell,
|
clipboard,
|
||||||
viewport,
|
shell,
|
||||||
);
|
viewport,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if !shell.is_event_captured() {
|
if !shell.is_event_captured() {
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left))
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
| Event::Touch(touch::Event::FingerLifted { .. }) => {
|
||||||
if cursor.is_over(layout.bounds()) {
|
if cursor.is_over(layout.bounds()) {
|
||||||
shell.publish(self.on_click.clone());
|
shell.publish(self.on_click.clone());
|
||||||
|
|
||||||
shell.capture_event();
|
shell.capture_event();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -253,13 +282,17 @@ where
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
) -> mouse::Interaction {
|
) -> mouse::Interaction {
|
||||||
let interaction = self.label.as_widget().mouse_interaction(
|
let interaction = if let Some(label) = &self.label {
|
||||||
&tree.children[0],
|
label.as_widget().mouse_interaction(
|
||||||
layout.children().nth(1).unwrap(),
|
&tree.children[0],
|
||||||
cursor,
|
layout.children().nth(1).unwrap(),
|
||||||
viewport,
|
cursor,
|
||||||
renderer,
|
viewport,
|
||||||
);
|
renderer,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mouse::Interaction::default()
|
||||||
|
};
|
||||||
|
|
||||||
if interaction == mouse::Interaction::default() {
|
if interaction == mouse::Interaction::default() {
|
||||||
if cursor.is_over(layout.bounds()) {
|
if cursor.is_over(layout.bounds()) {
|
||||||
|
|
@ -284,8 +317,6 @@ where
|
||||||
) {
|
) {
|
||||||
let is_mouse_over = cursor.is_over(layout.bounds());
|
let is_mouse_over = cursor.is_over(layout.bounds());
|
||||||
|
|
||||||
let mut children = layout.children();
|
|
||||||
|
|
||||||
let custom_style = if is_mouse_over {
|
let custom_style = if is_mouse_over {
|
||||||
theme.style(
|
theme.style(
|
||||||
&(),
|
&(),
|
||||||
|
|
@ -302,16 +333,21 @@ where
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
{
|
let (dot_bounds, label_layout) = if self.label.is_some() {
|
||||||
let layout = children.next().unwrap();
|
let mut children = layout.children();
|
||||||
let bounds = layout.bounds();
|
let dot_bounds = children.next().unwrap().bounds();
|
||||||
|
(dot_bounds, children.next())
|
||||||
|
} else {
|
||||||
|
(layout.bounds(), None)
|
||||||
|
};
|
||||||
|
|
||||||
let size = bounds.width;
|
{
|
||||||
|
let size = dot_bounds.width;
|
||||||
let dot_size = 6.0;
|
let dot_size = 6.0;
|
||||||
|
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds,
|
bounds: dot_bounds,
|
||||||
border: Border {
|
border: Border {
|
||||||
radius: (size / 2.0).into(),
|
radius: (size / 2.0).into(),
|
||||||
width: custom_style.border_width,
|
width: custom_style.border_width,
|
||||||
|
|
@ -326,8 +362,8 @@ where
|
||||||
renderer.fill_quad(
|
renderer.fill_quad(
|
||||||
renderer::Quad {
|
renderer::Quad {
|
||||||
bounds: Rectangle {
|
bounds: Rectangle {
|
||||||
x: bounds.x + (size - dot_size) / 2.0,
|
x: dot_bounds.x + (size - dot_size) / 2.0,
|
||||||
y: bounds.y + (size - dot_size) / 2.0,
|
y: dot_bounds.y + (size - dot_size) / 2.0,
|
||||||
width: dot_size,
|
width: dot_size,
|
||||||
height: dot_size,
|
height: dot_size,
|
||||||
},
|
},
|
||||||
|
|
@ -339,9 +375,8 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
if let (Some(label), Some(label_layout)) = (&self.label, label_layout) {
|
||||||
let label_layout = children.next().unwrap();
|
label.as_widget().draw(
|
||||||
self.label.as_widget().draw(
|
|
||||||
&tree.children[0],
|
&tree.children[0],
|
||||||
renderer,
|
renderer,
|
||||||
theme,
|
theme,
|
||||||
|
|
@ -361,7 +396,7 @@ where
|
||||||
viewport: &Rectangle,
|
viewport: &Rectangle,
|
||||||
translation: Vector,
|
translation: Vector,
|
||||||
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
) -> Option<overlay::Element<'b, Message, Theme, Renderer>> {
|
||||||
self.label.as_widget_mut().overlay(
|
self.label.as_mut()?.as_widget_mut().overlay(
|
||||||
&mut tree.children[0],
|
&mut tree.children[0],
|
||||||
layout.children().nth(1).unwrap(),
|
layout.children().nth(1).unwrap(),
|
||||||
renderer,
|
renderer,
|
||||||
|
|
@ -377,12 +412,14 @@ where
|
||||||
renderer: &Renderer,
|
renderer: &Renderer,
|
||||||
dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
|
dnd_rectangles: &mut iced_core::clipboard::DndDestinationRectangles,
|
||||||
) {
|
) {
|
||||||
self.label.as_widget().drag_destinations(
|
if let Some(label) = &self.label {
|
||||||
&state.children[0],
|
label.as_widget().drag_destinations(
|
||||||
layout.children().nth(1).unwrap(),
|
&state.children[0],
|
||||||
renderer,
|
layout.children().nth(1).unwrap(),
|
||||||
dnd_rectangles,
|
renderer,
|
||||||
);
|
dnd_rectangles,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ use std::borrow::Cow;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Element, Theme, theme,
|
Element, Theme, theme,
|
||||||
widget::{FlexRow, Row, column, container, flex_row, row, text},
|
widget::{FlexRow, Row, column, container, flex_row, list, row, text},
|
||||||
};
|
};
|
||||||
use derive_setters::Setters;
|
use derive_setters::Setters;
|
||||||
use iced_core::{Length, text::Wrapping};
|
use iced_core::{Length, text::Wrapping};
|
||||||
|
|
@ -103,7 +103,7 @@ pub struct Item<'a, Message> {
|
||||||
icon: Option<Element<'a, Message>>,
|
icon: Option<Element<'a, Message>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message: 'static> Item<'a, Message> {
|
impl<'a, Message: Clone + 'static> Item<'a, Message> {
|
||||||
/// Assigns a control to the item.
|
/// Assigns a control to the item.
|
||||||
pub fn control(self, widget: impl Into<Element<'a, Message>>) -> Row<'a, Message, Theme> {
|
pub fn control(self, widget: impl Into<Element<'a, Message>>) -> Row<'a, Message, Theme> {
|
||||||
item_row(self.control_(widget.into()))
|
item_row(self.control_(widget.into()))
|
||||||
|
|
@ -114,39 +114,109 @@ impl<'a, Message: 'static> Item<'a, Message> {
|
||||||
flex_item_row(self.control_(widget.into()))
|
flex_item_row(self.control_(widget.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(never)]
|
fn label(self) -> Element<'a, Message> {
|
||||||
fn control_(self, widget: Element<'a, Message>) -> Vec<Element<'a, Message>> {
|
|
||||||
let mut contents = Vec::with_capacity(4);
|
|
||||||
|
|
||||||
if let Some(icon) = self.icon {
|
|
||||||
contents.push(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(description) = self.description {
|
if let Some(description) = self.description {
|
||||||
let column = column::with_capacity(2)
|
column::with_capacity(2)
|
||||||
.spacing(2)
|
.spacing(2)
|
||||||
.push(text::body(self.title).wrapping(Wrapping::Word))
|
.push(text::body(self.title).wrapping(Wrapping::Word))
|
||||||
.push(text::caption(description).wrapping(Wrapping::Word))
|
.push(text::caption(description).wrapping(Wrapping::Word))
|
||||||
.width(Length::Fill);
|
.width(Length::Fill)
|
||||||
|
.into()
|
||||||
contents.push(column.into());
|
|
||||||
} else {
|
} else {
|
||||||
contents.push(text(self.title).width(Length::Fill).into());
|
text(self.title).width(Length::Fill).into()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(never)]
|
||||||
|
fn control_(mut self, widget: Element<'a, Message>) -> Vec<Element<'a, Message>> {
|
||||||
|
let mut contents = Vec::with_capacity(3);
|
||||||
|
if let Some(icon) = self.icon.take() {
|
||||||
|
contents.push(icon);
|
||||||
|
}
|
||||||
|
contents.push(self.label());
|
||||||
contents.push(widget);
|
contents.push(widget);
|
||||||
contents
|
contents
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn control_start(self, widget: impl Into<Element<'a, Message>>) -> Row<'a, Message, Theme> {
|
||||||
|
item_row(vec![widget.into(), self.label()])
|
||||||
|
}
|
||||||
|
|
||||||
pub fn toggler(
|
pub fn toggler(
|
||||||
self,
|
self,
|
||||||
is_checked: bool,
|
is_checked: bool,
|
||||||
message: impl Fn(bool) -> Message + 'static,
|
message: impl Fn(bool) -> Message + 'static,
|
||||||
) -> Row<'a, Message, Theme> {
|
) -> list::ListButton<'a, Message> {
|
||||||
self.control(
|
let on_press = message(!is_checked);
|
||||||
crate::widget::toggler(is_checked)
|
list::button(
|
||||||
.width(Length::Shrink)
|
self.control(
|
||||||
.on_toggle(message),
|
crate::widget::toggler(is_checked)
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.on_toggle(message),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
|
.on_press(on_press)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggler_maybe(
|
||||||
|
self,
|
||||||
|
is_checked: bool,
|
||||||
|
message: Option<impl Fn(bool) -> Message + 'static>,
|
||||||
|
) -> list::ListButton<'a, Message> {
|
||||||
|
let on_press = message.as_ref().map(|f| f(!is_checked));
|
||||||
|
list::button(
|
||||||
|
self.control(
|
||||||
|
crate::widget::toggler(is_checked)
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.on_toggle_maybe(message),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_press_maybe(on_press)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checkbox(
|
||||||
|
self,
|
||||||
|
is_checked: bool,
|
||||||
|
message: impl Fn(bool) -> Message + 'static,
|
||||||
|
) -> list::ListButton<'a, Message> {
|
||||||
|
let on_press = message(!is_checked);
|
||||||
|
list::button(
|
||||||
|
self.control_start(
|
||||||
|
crate::widget::checkbox(is_checked)
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.on_toggle(message),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_press(on_press)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn checkbox_maybe(
|
||||||
|
self,
|
||||||
|
is_checked: bool,
|
||||||
|
message: Option<impl Fn(bool) -> Message + 'static>,
|
||||||
|
) -> list::ListButton<'a, Message> {
|
||||||
|
let on_press = message.as_ref().map(|f| f(!is_checked));
|
||||||
|
list::button(
|
||||||
|
self.control_start(
|
||||||
|
crate::widget::checkbox(is_checked)
|
||||||
|
.width(Length::Shrink)
|
||||||
|
.on_toggle_maybe(message),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.on_press_maybe(on_press)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn radio<V, F>(self, value: V, selected: Option<V>, f: F) -> list::ListButton<'a, Message>
|
||||||
|
where
|
||||||
|
V: Eq + Copy,
|
||||||
|
F: Fn(V) -> Message,
|
||||||
|
{
|
||||||
|
let on_press = f(value);
|
||||||
|
list::button(
|
||||||
|
self.control_start(crate::widget::radio::Radio::new_no_label(
|
||||||
|
value, selected, f,
|
||||||
|
)),
|
||||||
|
)
|
||||||
|
.on_press(on_press)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,16 +2,24 @@
|
||||||
// SPDX-License-Identifier: MPL-2.0
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
|
||||||
use crate::Element;
|
use crate::Element;
|
||||||
use crate::widget::{ListColumn, column, text};
|
use crate::widget::list_column::IntoListItem;
|
||||||
|
use crate::widget::{ListColumn, column, list_column, text};
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
|
||||||
/// A section within a settings view column.
|
/// A section within a settings view column.
|
||||||
pub fn section<'a, Message: 'static>() -> Section<'a, Message> {
|
pub fn section<'a, Message: Clone + 'static>() -> Section<'a, Message> {
|
||||||
with_column(ListColumn::default())
|
with_column(ListColumn::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A section with a pre-defined list column of a given capacity.
|
||||||
|
pub fn with_capacity<'a, Message: Clone + 'static>(capacity: usize) -> Section<'a, Message> {
|
||||||
|
with_column(list_column::with_capacity(capacity))
|
||||||
|
}
|
||||||
|
|
||||||
/// A section with a pre-defined list column.
|
/// A section with a pre-defined list column.
|
||||||
pub fn with_column<Message: 'static>(children: ListColumn<'_, Message>) -> Section<'_, Message> {
|
pub fn with_column<Message: Clone + 'static>(
|
||||||
|
children: ListColumn<'_, Message>,
|
||||||
|
) -> Section<'_, Message> {
|
||||||
Section {
|
Section {
|
||||||
header: None,
|
header: None,
|
||||||
children,
|
children,
|
||||||
|
|
@ -24,9 +32,9 @@ pub struct Section<'a, Message> {
|
||||||
children: ListColumn<'a, Message>,
|
children: ListColumn<'a, Message>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message: 'static> Section<'a, Message> {
|
impl<'a, Message: Clone + 'static> Section<'a, Message> {
|
||||||
/// Define an optional title for the section.
|
/// Define an optional title for the section.
|
||||||
pub fn title(mut self, title: impl Into<Cow<'a, str>>) -> Self {
|
pub fn title(self, title: impl Into<Cow<'a, str>>) -> Self {
|
||||||
self.header(text::heading(title.into()))
|
self.header(text::heading(title.into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,13 +46,13 @@ impl<'a, Message: 'static> Section<'a, Message> {
|
||||||
|
|
||||||
/// Add a child element to the section's list column.
|
/// Add a child element to the section's list column.
|
||||||
#[allow(clippy::should_implement_trait)]
|
#[allow(clippy::should_implement_trait)]
|
||||||
pub fn add(mut self, item: impl Into<Element<'a, Message>>) -> Self {
|
pub fn add(mut self, item: impl IntoListItem<'a, Message>) -> Self {
|
||||||
self.children = self.children.add(item.into());
|
self.children = self.children.add(item);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a child element to the section's list column, if `Some`.
|
/// Add a child element to the section's list column, if `Some`.
|
||||||
pub fn add_maybe(self, item: Option<impl Into<Element<'a, Message>>>) -> Self {
|
pub fn add_maybe(self, item: Option<impl IntoListItem<'a, Message>>) -> Self {
|
||||||
if let Some(item) = item {
|
if let Some(item) = item {
|
||||||
self.add(item)
|
self.add(item)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -55,13 +63,13 @@ impl<'a, Message: 'static> Section<'a, Message> {
|
||||||
/// Extends the [`Section`] with the given children.
|
/// Extends the [`Section`] with the given children.
|
||||||
pub fn extend(
|
pub fn extend(
|
||||||
self,
|
self,
|
||||||
children: impl IntoIterator<Item = impl Into<Element<'a, Message>>>,
|
children: impl IntoIterator<Item = impl IntoListItem<'a, Message>>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
children.into_iter().fold(self, Self::add)
|
children.into_iter().fold(self, Self::add)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Message: 'static> From<Section<'a, Message>> for Element<'a, Message> {
|
impl<'a, Message: Clone + 'static> From<Section<'a, Message>> for Element<'a, Message> {
|
||||||
fn from(data: Section<'a, Message>) -> Self {
|
fn from(data: Section<'a, Message>) -> Self {
|
||||||
column::with_capacity(2)
|
column::with_capacity(2)
|
||||||
.spacing(8)
|
.spacing(8)
|
||||||
|
|
|
||||||
|
|
@ -161,7 +161,10 @@ impl<'a, Message> Widget<Message, crate::Theme, crate::Renderer> for Toggler<'a,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn state(&self) -> tree::State {
|
fn state(&self) -> tree::State {
|
||||||
tree::State::new(State::default())
|
tree::State::new(State {
|
||||||
|
prev_toggled: self.is_toggled,
|
||||||
|
..State::default()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn id(&self) -> Option<Id> {
|
fn id(&self) -> Option<Id> {
|
||||||
|
|
@ -238,6 +241,14 @@ impl<'a, Message> Widget<Message, crate::Theme, crate::Renderer> for Toggler<'a,
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let state = tree.state.downcast_mut::<State>();
|
let state = tree.state.downcast_mut::<State>();
|
||||||
|
|
||||||
|
// animate external changes
|
||||||
|
if state.prev_toggled != self.is_toggled {
|
||||||
|
state.anim.changed(self.duration);
|
||||||
|
shell.request_redraw();
|
||||||
|
state.prev_toggled = self.is_toggled;
|
||||||
|
}
|
||||||
|
|
||||||
match event {
|
match event {
|
||||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left))
|
||||||
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
| Event::Touch(touch::Event::FingerPressed { .. }) => {
|
||||||
|
|
@ -246,6 +257,7 @@ impl<'a, Message> Widget<Message, crate::Theme, crate::Renderer> for Toggler<'a,
|
||||||
if mouse_over {
|
if mouse_over {
|
||||||
shell.publish((on_toggle)(!self.is_toggled));
|
shell.publish((on_toggle)(!self.is_toggled));
|
||||||
state.anim.changed(self.duration);
|
state.anim.changed(self.duration);
|
||||||
|
state.prev_toggled = !self.is_toggled;
|
||||||
shell.capture_event();
|
shell.capture_event();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -430,4 +442,5 @@ pub fn next_to_each_other(
|
||||||
pub struct State {
|
pub struct State {
|
||||||
text: widget::text::State<<crate::Renderer as iced_core::text::Renderer>::Paragraph>,
|
text: widget::text::State<<crate::Renderer as iced_core::text::Renderer>::Paragraph>,
|
||||||
anim: anim::State,
|
anim: anim::State,
|
||||||
|
prev_toggled: bool,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue