Compare commits
No commits in common. "pre-1.8-merge-20260910" and "master" have entirely different histories.
pre-1.8-me
...
master
259 changed files with 3643 additions and 9953 deletions
78
.github/workflows/ci.yml
vendored
78
.github/workflows/ci.yml
vendored
|
|
@ -6,20 +6,26 @@ on:
|
|||
- master
|
||||
pull_request:
|
||||
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v3
|
||||
- name: Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
rustflags: ""
|
||||
- name: Cargo cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }}
|
||||
- name: Format
|
||||
run: cargo +nightly fmt -- --check
|
||||
run: cargo fmt -- --check
|
||||
|
||||
tests:
|
||||
needs:
|
||||
|
|
@ -28,28 +34,38 @@ jobs:
|
|||
fail-fast: false
|
||||
matrix:
|
||||
test_args:
|
||||
- --no-default-features --features "" # for cosmic-comp, don't remove!
|
||||
- --no-default-features --features "winit_debug"
|
||||
- --no-default-features --features "winit_tokio"
|
||||
- --no-default-features --features "winit"
|
||||
- --no-default-features --features "winit_wgpu"
|
||||
- --no-default-features --features "wayland"
|
||||
- --no-default-features --features "applet"
|
||||
- --no-default-features --features "desktop,smol"
|
||||
- --no-default-features --features "desktop,tokio"
|
||||
- -p cosmic-theme
|
||||
runs-on: ubuntu-latest
|
||||
- --no-default-features --features "" # for cosmic-comp, don't remove!
|
||||
- --no-default-features --features "winit_debug"
|
||||
- --no-default-features --features "winit_tokio"
|
||||
- --no-default-features --features "winit"
|
||||
- --no-default-features --features "winit_wgpu"
|
||||
- --no-default-features --features "wayland"
|
||||
- --no-default-features --features "applet"
|
||||
- --no-default-features --features "desktop,smol"
|
||||
- --no-default-features --features "desktop,tokio"
|
||||
- -p cosmic-theme
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Get date for registry cache
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
|
||||
- name: Cargo registry cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }}
|
||||
restore-keys: ${{ runner.os }}-cargo-registry-
|
||||
- name: System dependencies
|
||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||
- name: Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
rustflags: ""
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Test features
|
||||
run: cargo test ${{ matrix.test_args }} -- --test-threads=1
|
||||
env:
|
||||
|
|
@ -66,18 +82,28 @@ jobs:
|
|||
- "open-dialog"
|
||||
- "context-menu"
|
||||
- "nav-context"
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Get date for registry cache
|
||||
id: date
|
||||
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
|
||||
- name: Cargo registry cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry/index
|
||||
~/.cargo/registry/cache
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-registry-${{ steps.date.outputs.date }}
|
||||
restore-keys: ${{ runner.os }}-cargo-registry-
|
||||
- name: System dependencies
|
||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||
- name: Rust toolchain
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
rustflags: ""
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
- name: Check example
|
||||
run: cargo check -p "${{ matrix.examples }}"
|
||||
env:
|
||||
|
|
|
|||
11
.github/workflows/pages.yml
vendored
11
.github/workflows/pages.yml
vendored
|
|
@ -11,26 +11,25 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Rust nightly
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2026-04-27
|
||||
rustflags: ""
|
||||
toolchain: nightly-2025-07-31
|
||||
- name: System dependencies
|
||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||
- name: Build documentation
|
||||
run: |
|
||||
RUSTDOCFLAGS="--cfg docsrs" \
|
||||
cargo +nightly-2026-04-27 doc --no-deps \
|
||||
cargo +nightly-2025-07-31 doc --no-deps \
|
||||
-p cosmic-client-toolkit \
|
||||
-p cosmic-protocols \
|
||||
-p libcosmic \
|
||||
--verbose --features tokio,winit,wayland,desktop,single-instance,applet,xdg-portal,multi-window
|
||||
- name: Deploy documentation
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./target/doc
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"format_on_save": "on",
|
||||
"lsp": {
|
||||
"rust-analyzer": {
|
||||
"initialization_options": {
|
||||
"check": {
|
||||
"command": "clippy",
|
||||
},
|
||||
"rustfmt": {
|
||||
"extraArgs": ["+nightly"],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
149
Cargo.toml
149
Cargo.toml
|
|
@ -1,23 +1,20 @@
|
|||
[package]
|
||||
# Yoda fork: hard-renamed. Every consumer (leyoda/cosmic-files fork + each
|
||||
# leyoda/cosmic-* app) depends directly on `libcosmic-yoda` by path, bypassing
|
||||
# pop-os/libcosmic entirely. No [patch] shenanigans needed — transitive deps
|
||||
# that used to ask for `libcosmic` are replaced by deps on our forks that ask
|
||||
# for `libcosmic-yoda`.
|
||||
name = "libcosmic-yoda"
|
||||
version = "0.1.0-yoda.2"
|
||||
name = "libcosmic"
|
||||
version = "1.0.0"
|
||||
edition = "2024"
|
||||
rust-version = "1.93"
|
||||
rust-version = "1.90"
|
||||
|
||||
[lib]
|
||||
name = "cosmic"
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"winit",
|
||||
"tokio",
|
||||
"a11y",
|
||||
"dbus-config",
|
||||
"wayland",
|
||||
"x11",
|
||||
"iced-wayland",
|
||||
"multi-window",
|
||||
]
|
||||
advanced-shaping = ["iced/advanced-shaping"]
|
||||
|
|
@ -38,6 +35,7 @@ animated-image = [
|
|||
autosize = []
|
||||
applet = [
|
||||
"autosize",
|
||||
"winit",
|
||||
"wayland",
|
||||
"tokio",
|
||||
"cosmic-panel-config",
|
||||
|
|
@ -74,7 +72,6 @@ serde-keycode = ["iced_core/serde"]
|
|||
single-instance = ["zbus/blocking-api", "ron"]
|
||||
# smol async runtime
|
||||
smol = ["dep:smol", "iced/smol", "zbus?/async-io", "rfd?/async-std"]
|
||||
# Tokio async runtime
|
||||
tokio = [
|
||||
"dep:tokio",
|
||||
"ashpd?/tokio",
|
||||
|
|
@ -84,25 +81,20 @@ tokio = [
|
|||
"cosmic-config/tokio",
|
||||
]
|
||||
# Tokio async runtime
|
||||
# Wayland window support (yoda fork is Wayland-only; always active in default).
|
||||
# We still need iced/winit because pop-os/iced hosts the runtime dispatcher
|
||||
# (`iced_winit as shell`) there — the name is a misnomer, it's the same crate
|
||||
# that provides both the winit path AND the sctk/cctk wayland path.
|
||||
# COSMIC 1.5/1.6 renamed the iced `cctk` features to `wayland`; the local
|
||||
# `winit` feature stays empty in this fork, so iced/winit is listed explicitly
|
||||
# instead of going through it.
|
||||
wayland = [
|
||||
# Wayland window support
|
||||
iced-wayland = [
|
||||
"ashpd?/wayland",
|
||||
"autosize",
|
||||
"multi-window",
|
||||
"surface-message",
|
||||
"winit",
|
||||
"iced/winit",
|
||||
"iced/wayland",
|
||||
"iced_winit/wayland",
|
||||
"iced_runtime/wayland",
|
||||
"iced_wgpu/wayland",
|
||||
"dep:iced_winit",
|
||||
"surface-message",
|
||||
]
|
||||
wayland = [
|
||||
"iced-wayland",
|
||||
"iced_runtime/cctk",
|
||||
"iced_winit/cctk",
|
||||
"iced_wgpu/cctk",
|
||||
"iced/cctk",
|
||||
"dep:cctk",
|
||||
]
|
||||
surface-message = []
|
||||
|
|
@ -110,11 +102,11 @@ surface-message = []
|
|||
multi-window = []
|
||||
# Render with wgpu
|
||||
wgpu = ["iced/wgpu", "iced_wgpu"]
|
||||
# Compat stubs — kept empty so upstream deps (cosmic-files, cosmic-text, …)
|
||||
# that still ask for `winit` / `x11` features resolve cleanly against the
|
||||
# yoda fork. Activating them has no effect: no code is gated on these.
|
||||
winit = []
|
||||
x11 = []
|
||||
# X11 window support via winit
|
||||
winit = ["iced/winit", "iced_winit"]
|
||||
winit_debug = ["winit", "debug"]
|
||||
winit_tokio = ["winit", "tokio"]
|
||||
winit_wgpu = ["winit", "wgpu"]
|
||||
# Enables XDG portal integrations
|
||||
xdg-portal = ["ashpd"]
|
||||
qr_code = ["iced/qr_code"]
|
||||
|
|
@ -127,15 +119,18 @@ async-std = [
|
|||
"zbus?/async-io",
|
||||
"iced/async-std",
|
||||
]
|
||||
x11 = ["iced/x11", "iced_winit/x11"]
|
||||
|
||||
[dependencies]
|
||||
apply = "0.3.0"
|
||||
ashpd = { version = "0.12.3", default-features = false, optional = true }
|
||||
async-fs = { version = "2.2", optional = true }
|
||||
async-std = { workspace = true, optional = true }
|
||||
async-std = { version = "1.13", optional = true }
|
||||
auto_enums = "0.8.8"
|
||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "160b086", optional = true }
|
||||
jiff = "0.2"
|
||||
cosmic-config = { path = "cosmic-config" }
|
||||
cosmic-settings-config = { path = "../cosmic-settings-daemon/config", optional = true }
|
||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
|
||||
# Internationalization
|
||||
i18n-embed = { version = "0.16.0", features = [
|
||||
"fluent-system",
|
||||
|
|
@ -155,38 +150,34 @@ image-extras = { version = "0.1.0", default-features = false, features = [
|
|||
"xpm",
|
||||
"xbm",
|
||||
], optional = true }
|
||||
libc = { version = "0.2.186", optional = true }
|
||||
libc = { version = "0.2.183", optional = true }
|
||||
log = "0.4"
|
||||
mime = { version = "0.3.17", optional = true }
|
||||
palette.workspace = true
|
||||
palette = "0.7.6"
|
||||
rfd = { version = "0.16.0", default-features = false, features = [
|
||||
"xdg-portal",
|
||||
], optional = true }
|
||||
rustix = { version = "1.1", features = ["pipe", "process"], optional = true }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
slotmap = "1.1.1"
|
||||
smol = { version = "2.0.2", optional = true }
|
||||
thiserror.workspace = true
|
||||
thiserror = "2.0.18"
|
||||
taffy = { version = "0.9.2", features = ["grid"] }
|
||||
tokio = { workspace = true, optional = true }
|
||||
tracing.workspace = true
|
||||
unicode-segmentation = "1.13"
|
||||
tokio = { version = "1.50.0", optional = true }
|
||||
tracing = "0.1.44"
|
||||
unicode-segmentation = "1.12"
|
||||
url = "2.5.8"
|
||||
zbus = { workspace = true, optional = true }
|
||||
zbus = { version = "5.14.0", default-features = false, optional = true }
|
||||
float-cmp = "0.10.0"
|
||||
ron = { workspace = true, optional = true }
|
||||
enumflags2 = "0.7.12"
|
||||
|
||||
# Enable DBus feature on Linux targets
|
||||
[target.'cfg(all(unix, not(any(target_os = "redox", target_family = "wasm", target_os = "android", target_vendor = "apple"))))'.dependencies]
|
||||
cctk = { path = "../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit", optional = true }
|
||||
ashpd = { version = "0.12.3", default-features = false, optional = true }
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
cosmic-config = { path = "cosmic-config", features = ["dbus"] }
|
||||
cosmic-settings-daemon = { path = "../dbus-settings-bindings/cosmic-settings-daemon" }
|
||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||
zbus = { version = "5.14.0", default-features = false }
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
||||
freedesktop-icons = { package = "cosmic-freedesktop-icons", path = "../cosmic-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 }
|
||||
shlex = { version = "1.3.0", optional = true }
|
||||
|
||||
|
|
@ -232,83 +223,35 @@ optional = true
|
|||
|
||||
[dependencies.iced_tiny_skia]
|
||||
path = "./iced/tiny_skia"
|
||||
# Yoda: drop the x11 default → softbuffer no longer pulls tiny-xlib/x11-dl/etc.
|
||||
default-features = false
|
||||
features = ["wayland"]
|
||||
|
||||
[dependencies.iced_winit]
|
||||
path = "./iced/winit"
|
||||
optional = true
|
||||
# Yoda: drop the x11 default → winit won't pull winit-x11/tiny-xlib/x11-dl.
|
||||
# Keep wayland + wayland-dlopen (default behaviour minus x11).
|
||||
default-features = false
|
||||
features = ["wayland", "wayland-dlopen"]
|
||||
|
||||
[dependencies.iced_wgpu]
|
||||
path = "./iced/wgpu"
|
||||
optional = true
|
||||
|
||||
[dependencies.cosmic-panel-config]
|
||||
path = "../cosmic-panel/cosmic-panel-config"
|
||||
git = "https://github.com/pop-os/cosmic-panel"
|
||||
# path = "../cosmic-panel/cosmic-panel-config"
|
||||
optional = true
|
||||
|
||||
[patch.'https://github.com/pop-os/freedesktop-icons']
|
||||
cosmic-freedesktop-icons = { path = "../cosmic-freedesktop-icons" }
|
||||
|
||||
[patch.'https://github.com/pop-os/softbuffer']
|
||||
softbuffer = { path = "../softbuffer" }
|
||||
|
||||
[patch.'https://github.com/pop-os/smithay-clipboard']
|
||||
smithay-clipboard = { path = "../smithay-clipboard" }
|
||||
|
||||
[patch.'https://github.com/pop-os/winit.git']
|
||||
dpi = { path = "../winit/dpi" }
|
||||
winit = { path = "../winit/winit" }
|
||||
winit-android = { path = "../winit/winit-android" }
|
||||
winit-appkit = { path = "../winit/winit-appkit" }
|
||||
winit-common = { path = "../winit/winit-common" }
|
||||
winit-core = { path = "../winit/winit-core" }
|
||||
winit-orbital = { path = "../winit/winit-orbital" }
|
||||
winit-uikit = { path = "../winit/winit-uikit" }
|
||||
winit-wayland = { path = "../winit/winit-wayland" }
|
||||
winit-web = { path = "../winit/winit-web" }
|
||||
winit-win32 = { path = "../winit/winit-win32" }
|
||||
winit-x11 = { path = "../winit/winit-x11" }
|
||||
[dependencies.ron]
|
||||
version = "0.12"
|
||||
optional = true
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"cosmic-config",
|
||||
"cosmic-config-derive",
|
||||
"cosmic-theme",
|
||||
"examples/*",
|
||||
]
|
||||
# examples/* excluded — many depend on the removed winit/x11 features.
|
||||
# They will be revisited and adapted in a later phase.
|
||||
exclude = ["iced", "examples"]
|
||||
exclude = ["iced"]
|
||||
|
||||
[workspace.dependencies]
|
||||
async-std = "1.13"
|
||||
dirs = "6.0"
|
||||
palette = "0.7"
|
||||
ron = "0.12"
|
||||
serde = "1.0"
|
||||
thiserror = "2.0"
|
||||
tracing = "0.1"
|
||||
tokio = "1.52"
|
||||
zbus = { version = "5.15", default-features = false }
|
||||
|
||||
# Speed up snapshot diffing in cosmic-theme tests. Cargo silently ignores
|
||||
# [profile.*] blocks in non-root manifests, so this lives at the
|
||||
# workspace root.
|
||||
[profile.dev.package.insta]
|
||||
opt-level = 3
|
||||
[profile.dev.package.similar]
|
||||
opt-level = 3
|
||||
dirs = "6.0.0"
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.27.0"
|
||||
|
||||
[workspace.package]
|
||||
edition = "2024"
|
||||
|
||||
[build-dependencies]
|
||||
build_helpers = { path = "./iced/build_helpers" }
|
||||
|
|
|
|||
18
README.md
18
README.md
|
|
@ -20,24 +20,6 @@ While libcosmic is written entirely in Rust, some of its dependencies may requir
|
|||
sudo apt install cargo cmake just libexpat1-dev libfontconfig-dev libfreetype-dev libxkbcommon-dev pkgconf
|
||||
```
|
||||
|
||||
## Made-for-COSMIC Flatpak IDs
|
||||
|
||||
To identify a project as a COSMIC Application, add `<id>com.system76.CosmicApplication</id>` to the provides section of the project's metainfo.
|
||||
|
||||
```xml
|
||||
<provides>
|
||||
<id>com.system76.CosmicApplication</id>
|
||||
</provides>
|
||||
```
|
||||
|
||||
For COSMIC Applets, use `com.system76.CosmicApplet`.
|
||||
|
||||
```xml
|
||||
<provides>
|
||||
<id>com.system76.CosmicApplet</id>
|
||||
</provides>
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
Some examples are included in the [examples](./examples) directory to to kickstart your
|
||||
|
|
|
|||
1
build.rs
1
build.rs
|
|
@ -8,7 +8,6 @@ fn main() {
|
|||
{
|
||||
generate_bundled_icons();
|
||||
}
|
||||
build_helpers::cfg_aliases_setup();
|
||||
}
|
||||
|
||||
fn generate_bundled_icons() {
|
||||
|
|
|
|||
|
|
@ -1,32 +1,17 @@
|
|||
use proc_macro::TokenStream;
|
||||
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 {
|
||||
// Construct a representation of Rust code as a syntax tree
|
||||
// that we can manipulate
|
||||
let ast = syn::parse(input).unwrap();
|
||||
|
||||
// Build the trait implementation
|
||||
impl_cosmic_config_entry_macro(&ast)
|
||||
}
|
||||
|
||||
fn get_cosmic_config_attrs(field: &syn::Field) -> Result<Option<syn::Type>, syn::Error> {
|
||||
let mut with = None;
|
||||
|
||||
for attr in &field.attrs {
|
||||
if !attr.path().is_ident("cosmic_config_entry") {
|
||||
continue;
|
||||
}
|
||||
attr.parse_nested_meta(|meta| {
|
||||
if meta.path.is_ident("with") {
|
||||
let value = meta.value()?;
|
||||
with = Some(value.parse()?);
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(with)
|
||||
}
|
||||
|
||||
fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||
let attributes = &ast.attrs;
|
||||
let version = attributes
|
||||
|
|
@ -63,54 +48,19 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||
|
||||
let write_each_config_field = fields.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
let with = match get_cosmic_config_attrs(field) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => {
|
||||
return e.to_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(with) = with {
|
||||
quote! {
|
||||
{
|
||||
let conv = self.#field_name.clone().into();
|
||||
cosmic_config::ConfigSet::set::<#with>(&tx, stringify!(#field_name), conv)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
|
||||
}
|
||||
quote! {
|
||||
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
|
||||
}
|
||||
});
|
||||
|
||||
let get_each_config_field = fields.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
let field_type = &field.ty;
|
||||
let with = match get_cosmic_config_attrs(field) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => {
|
||||
return e.to_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(with) = with {
|
||||
quote! {
|
||||
match cosmic_config::ConfigGet::get::<#with>(config, stringify!(#field_name)) {
|
||||
Ok(value) => {
|
||||
default.#field_name = value.into();
|
||||
}
|
||||
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||
Ok(#field_name) => default.#field_name = #field_name,
|
||||
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
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),
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -118,39 +68,17 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||
let update_each_config_field = fields.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
let field_type = &field.ty;
|
||||
let with = match get_cosmic_config_attrs(field) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => {
|
||||
return e.to_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(with) = with {
|
||||
quote! {
|
||||
stringify!(#field_name) => {
|
||||
match cosmic_config::ConfigGet::get::<#with>(config, stringify!(#field_name)) {
|
||||
Ok(value) => {
|
||||
let value = value.into();
|
||||
if self.#field_name != value {
|
||||
keys.push(stringify!(#field_name));
|
||||
}
|
||||
self.#field_name = value;
|
||||
},
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
stringify!(#field_name) => {
|
||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||
Ok(value) => {
|
||||
if self.#field_name != value {
|
||||
keys.push(stringify!(#field_name));
|
||||
}
|
||||
self.#field_name = value;
|
||||
},
|
||||
Err(e) => errors.push(e),
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,21 +10,21 @@ macro = ["cosmic-config-derive"]
|
|||
subscription = ["iced_futures"]
|
||||
|
||||
[dependencies]
|
||||
cosmic-settings-daemon = { path = "../../dbus-settings-bindings/cosmic-settings-daemon", optional = true }
|
||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
||||
zbus = { version = "5.14.0", default-features = false, optional = true }
|
||||
atomicwrites = { git = "https://github.com/jackpot51/rust-atomicwrites" }
|
||||
calloop = { version = "0.14.4", optional = true }
|
||||
notify = "8.2.0"
|
||||
ron.workspace = true
|
||||
serde.workspace = true
|
||||
ron = "0.12.0"
|
||||
serde = "1.0.228"
|
||||
cosmic-config-derive = { path = "../cosmic-config-derive/", optional = true }
|
||||
iced = { path = "../iced/", default-features = false, optional = true }
|
||||
iced_futures = { path = "../iced/futures/", default-features = false, optional = true }
|
||||
futures-util = { version = "0.3", optional = true }
|
||||
dirs.workspace = true
|
||||
tokio = { workspace = true, optional = true, features = ["time"] }
|
||||
async-std = { workspace = true, optional = true }
|
||||
tracing.workspace = true
|
||||
tokio = { version = "1.50", optional = true, features = ["time"] }
|
||||
async-std = { version = "1.13", optional = true }
|
||||
tracing = "0.1"
|
||||
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
xdg = "3.0"
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
use std::any::TypeId;
|
||||
use std::ops::Deref;
|
||||
use std::{any::TypeId, ops::Deref};
|
||||
|
||||
use crate::{CosmicConfigEntry, Update};
|
||||
use cosmic_settings_daemon::{Changed, ConfigProxy, CosmicSettingsDaemonProxy};
|
||||
use futures_util::SinkExt;
|
||||
use iced_futures::futures::future::pending;
|
||||
use iced_futures::futures::{self, StreamExt};
|
||||
use iced_futures::{Subscription, stream};
|
||||
use iced_futures::{
|
||||
Subscription,
|
||||
futures::{self, StreamExt, future::pending},
|
||||
stream,
|
||||
};
|
||||
|
||||
pub async fn settings_daemon_proxy() -> zbus::Result<CosmicSettingsDaemonProxy<'static>> {
|
||||
let conn = zbus::Connection::session().await?;
|
||||
|
|
@ -67,8 +68,6 @@ struct Wrapper(
|
|||
impl std::hash::Hash for Wrapper {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
self.2.hash(state);
|
||||
self.3.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
//! Integrations for cosmic-config — the cosmic configuration system.
|
||||
|
||||
use notify::event::{EventKind, ModifyKind, RenameMode};
|
||||
use notify::{RecommendedWatcher, Watcher};
|
||||
use serde::Serialize;
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::Mutex;
|
||||
use std::{env, fmt, fs};
|
||||
use notify::{
|
||||
RecommendedWatcher, Watcher,
|
||||
event::{EventKind, ModifyKind, RenameMode},
|
||||
};
|
||||
use serde::{Serialize, de::DeserializeOwned};
|
||||
use std::{
|
||||
env, fmt, fs,
|
||||
io::Write,
|
||||
path::{Path, PathBuf},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
/// Get the config directory, with Flatpak sandbox support.
|
||||
/// In Flatpak, HOST_XDG_CONFIG_HOME points to the real user config directory,
|
||||
|
|
@ -48,12 +51,7 @@ fn get_state_dir() -> Option<PathBuf> {
|
|||
return Some(PathBuf::from(home).join(".local").join("state"));
|
||||
}
|
||||
}
|
||||
if let Some(state_dir) = dirs::state_dir() {
|
||||
return Some(state_dir);
|
||||
}
|
||||
// On platforms without state_dir, fall back to ~/.local/state
|
||||
let home_dir = dirs::home_dir()?;
|
||||
Some(home_dir.join(".local").join(".state"))
|
||||
dirs::state_dir()
|
||||
}
|
||||
|
||||
#[cfg(feature = "subscription")]
|
||||
|
|
@ -164,7 +162,6 @@ pub trait ConfigSet {
|
|||
pub struct Config {
|
||||
system_path: Option<PathBuf>,
|
||||
user_path: Option<PathBuf>,
|
||||
previous: Option<Box<Config>>,
|
||||
}
|
||||
|
||||
/// Check that the name is relative and doesn't contain . or ..
|
||||
|
|
@ -183,13 +180,9 @@ fn sanitize_name(name: &str) -> Result<&Path, Error> {
|
|||
impl Config {
|
||||
/// Get a system config for the given name and config version
|
||||
pub fn system(name: &str, version: u64) -> Result<Self, Error> {
|
||||
Self::system_inner(name, version, true)
|
||||
}
|
||||
|
||||
fn system_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||
#[cfg(unix)]
|
||||
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(&path);
|
||||
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(path);
|
||||
|
||||
#[cfg(windows)]
|
||||
let system_path =
|
||||
|
|
@ -199,13 +192,6 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path,
|
||||
user_path: None,
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::system_inner(name, version - 1, false)
|
||||
.ok()
|
||||
.map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -213,10 +199,6 @@ impl Config {
|
|||
// Use folder at XDG config/name for config storage, return Config if successful
|
||||
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||
pub fn new(name: &str, version: u64) -> Result<Self, Error> {
|
||||
Self::new_inner(name, version, true)
|
||||
}
|
||||
|
||||
fn new_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||
// Look for [name]/v[version]
|
||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||
|
||||
|
|
@ -241,29 +223,15 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path,
|
||||
user_path: Some(user_path),
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::new_inner(name, version - 1, false).ok().map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Get config for the given application name and config version and custom path.
|
||||
pub fn with_custom_path(name: &str, version: u64, custom_path: PathBuf) -> Result<Self, Error> {
|
||||
Self::with_custom_path_inner(name, version, custom_path, true)
|
||||
}
|
||||
|
||||
fn with_custom_path_inner(
|
||||
name: &str,
|
||||
version: u64,
|
||||
custom_path: PathBuf,
|
||||
look_for_previous: bool,
|
||||
) -> Result<Self, Error> {
|
||||
// Look for [name]/v[version]
|
||||
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||
|
||||
let mut user_path = custom_path.clone();
|
||||
let mut user_path = custom_path;
|
||||
user_path.push("cosmic");
|
||||
user_path.push(path);
|
||||
// Create new configuration directory if not found.
|
||||
|
|
@ -273,13 +241,6 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path: None,
|
||||
user_path: Some(user_path),
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::with_custom_path_inner(name, version - 1, custom_path.clone(), false)
|
||||
.ok()
|
||||
.map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -289,10 +250,6 @@ impl Config {
|
|||
// Use folder at XDG config/name for config storage, return Config if successful
|
||||
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||
pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
|
||||
Self::new_state_inner(name, version, true)
|
||||
}
|
||||
|
||||
fn new_state_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||
// Look for [name]/v[version]
|
||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||
|
||||
|
|
@ -306,13 +263,6 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path: None,
|
||||
user_path: Some(user_path),
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::new_state_inner(name, version - 1, false)
|
||||
.ok()
|
||||
.map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -423,13 +373,7 @@ impl ConfigGet for Config {
|
|||
Ok(ron::from_str(&data)?)
|
||||
}
|
||||
|
||||
_ => {
|
||||
if let Some(previous) = self.previous.as_ref() {
|
||||
previous.get_local(key)
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
_ => Err(Error::NotFound),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
use iced_futures::futures::channel::mpsc;
|
||||
use iced_futures::futures::{SinkExt, Stream};
|
||||
use iced_futures::stream;
|
||||
use iced_futures::{futures::channel::mpsc, stream};
|
||||
use notify::RecommendedWatcher;
|
||||
use std::borrow::Cow;
|
||||
use std::hash::Hash;
|
||||
use std::{borrow::Cow, hash::Hash};
|
||||
|
||||
use crate::{Config, CosmicConfigEntry};
|
||||
|
||||
|
|
@ -79,8 +77,7 @@ async fn start_listening<T: 'static + Send + Sync + PartialEq + Clone + CosmicCo
|
|||
state: ConfigState<T>,
|
||||
output: &mut mpsc::Sender<crate::Update<T>>,
|
||||
) -> ConfigState<T> {
|
||||
use iced_futures::futures::StreamExt;
|
||||
use iced_futures::futures::future::pending;
|
||||
use iced_futures::futures::{StreamExt, future::pending};
|
||||
|
||||
match state {
|
||||
ConfigState::Init(config_id, version, is_state) => {
|
||||
|
|
|
|||
|
|
@ -15,14 +15,13 @@ export = ["serde_json"]
|
|||
no-default = []
|
||||
|
||||
[dependencies]
|
||||
palette = { workspace = true, features = ["serializing"] }
|
||||
hex_color = { version = "3", features = ["serde"] }
|
||||
palette = { version = "0.7.6", features = ["serializing"] }
|
||||
almost = "0.2"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = { version = "1.0.149", optional = true, features = [
|
||||
"preserve_order",
|
||||
] }
|
||||
ron.workspace = true
|
||||
ron = "0.12.0"
|
||||
csscolorparser = { version = "0.8.3", features = ["serde"] }
|
||||
cosmic-config = { path = "../cosmic-config/", default-features = false, features = [
|
||||
"subscription",
|
||||
|
|
@ -30,8 +29,11 @@ cosmic-config = { path = "../cosmic-config/", default-features = false, features
|
|||
] }
|
||||
configparser = "3.1.0"
|
||||
dirs.workspace = true
|
||||
thiserror.workspace = true
|
||||
thiserror = "2.0.18"
|
||||
|
||||
[dev-dependencies]
|
||||
insta = "1.47.2"
|
||||
|
||||
[profile.dev.package]
|
||||
insta.opt-level = 3
|
||||
similar.opt-level = 3
|
||||
|
|
|
|||
|
|
@ -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) => Some(v.to_repr()).serialize(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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -29,51 +29,3 @@ impl Default for CornerRadii {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Roundness options for the Cosmic theme
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Roundness {
|
||||
/// Round style
|
||||
#[default]
|
||||
Round,
|
||||
/// Slightly round style
|
||||
SlightlyRound,
|
||||
/// Square style
|
||||
Square,
|
||||
}
|
||||
|
||||
impl From<Roundness> for CornerRadii {
|
||||
fn from(value: Roundness) -> Self {
|
||||
match value {
|
||||
Roundness::Round => CornerRadii::default(),
|
||||
Roundness::SlightlyRound => CornerRadii {
|
||||
radius_0: [0.0; 4],
|
||||
radius_xs: [2.0; 4],
|
||||
radius_s: [8.0; 4],
|
||||
radius_m: [8.0; 4],
|
||||
radius_l: [8.0; 4],
|
||||
radius_xl: [8.0; 4],
|
||||
},
|
||||
Roundness::Square => CornerRadii {
|
||||
radius_0: [0.0; 4],
|
||||
radius_xs: [2.0; 4],
|
||||
radius_s: [2.0; 4],
|
||||
radius_m: [2.0; 4],
|
||||
radius_l: [2.0; 4],
|
||||
radius_xl: [2.0; 4],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CornerRadii> for Roundness {
|
||||
fn from(value: CornerRadii) -> Self {
|
||||
if (value.radius_m[0] - 16.0).abs() < 0.01 {
|
||||
Self::Round
|
||||
} else if (value.radius_m[0] - 8.0).abs() < 0.01 {
|
||||
Self::SlightlyRound
|
||||
} else {
|
||||
Self::Square
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
use crate::color::color_serde;
|
||||
use palette::Srgba;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::LazyLock;
|
||||
|
|
@ -96,107 +95,75 @@ pub struct CosmicPaletteInner {
|
|||
|
||||
/// Utility Colors
|
||||
/// Colors used for various points of emphasis in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub bright_red: Srgba,
|
||||
/// Colors used for various points of emphasis in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub bright_green: Srgba,
|
||||
/// Colors used for various points of emphasis in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub bright_orange: Srgba,
|
||||
|
||||
/// Surface Grays
|
||||
/// Colors used for three levels of surfaces in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub gray_1: Srgba,
|
||||
/// Colors used for three levels of surfaces in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub gray_2: Srgba,
|
||||
|
||||
/// System Neutrals
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_0: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_1: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_2: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_3: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_4: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_5: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_6: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_7: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_8: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_9: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_10: Srgba,
|
||||
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_blue: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_indigo: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_purple: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_pink: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_red: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_orange: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_yellow: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_green: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_warm_grey: Srgba,
|
||||
|
||||
/// Extended Color Palette
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_warm_grey: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_orange: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_yellow: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_blue: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_purple: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_pink: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_indigo: Srgba,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
69
cosmic-theme/src/model/density.rs
Normal file
69
cosmic-theme/src/model/density.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use crate::Spacing;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// Density options for the Cosmic theme
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Density {
|
||||
/// Lower padding/spacing of elements
|
||||
Compact,
|
||||
/// Higher padding/spacing of elements
|
||||
Spacious,
|
||||
/// Standard padding/spacing of elements
|
||||
#[default]
|
||||
Standard,
|
||||
}
|
||||
|
||||
impl From<Density> for Spacing {
|
||||
fn from(value: Density) -> Self {
|
||||
match value {
|
||||
Density::Compact => Spacing {
|
||||
space_none: 0,
|
||||
space_xxxs: 4,
|
||||
space_xxs: 4,
|
||||
space_xs: 8,
|
||||
space_s: 8,
|
||||
space_m: 16,
|
||||
space_l: 24,
|
||||
space_xl: 32,
|
||||
space_xxl: 48,
|
||||
space_xxxl: 64,
|
||||
},
|
||||
Density::Spacious => Spacing {
|
||||
space_none: 4,
|
||||
space_xxxs: 8,
|
||||
space_xxs: 12,
|
||||
space_xs: 16,
|
||||
space_s: 24,
|
||||
space_m: 32,
|
||||
space_l: 48,
|
||||
space_xl: 64,
|
||||
space_xxl: 128,
|
||||
space_xxxl: 160,
|
||||
},
|
||||
Density::Standard => Spacing {
|
||||
space_none: 0,
|
||||
space_xxxs: 4,
|
||||
space_xxs: 8,
|
||||
space_xs: 12,
|
||||
space_s: 16,
|
||||
space_m: 24,
|
||||
space_l: 32,
|
||||
space_xl: 48,
|
||||
space_xxl: 64,
|
||||
space_xxxl: 128,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Spacing> for Density {
|
||||
fn from(value: Spacing) -> Self {
|
||||
if value.space_m.saturating_sub(16) == 0 {
|
||||
Self::Compact
|
||||
} else if value.space_m.saturating_sub(24) == 0 {
|
||||
Self::Standard
|
||||
} else {
|
||||
Self::Spacious
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
use crate::color::color_serde;
|
||||
use palette::{Srgba, WithAlpha};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -9,18 +8,14 @@ use crate::composite::over;
|
|||
#[must_use]
|
||||
pub struct Container {
|
||||
/// the color of the container
|
||||
#[serde(with = "color_serde")]
|
||||
pub base: Srgba,
|
||||
/// the color of components in the container
|
||||
pub component: Component,
|
||||
/// the color of dividers in the container
|
||||
#[serde(with = "color_serde")]
|
||||
pub divider: Srgba,
|
||||
/// the color of text in the container
|
||||
#[serde(with = "color_serde")]
|
||||
pub on: Srgba,
|
||||
/// the color of @small_widget_container
|
||||
#[serde(with = "color_serde")]
|
||||
pub small_widget: Srgba,
|
||||
}
|
||||
|
||||
|
|
@ -50,42 +45,30 @@ impl Container {
|
|||
#[must_use]
|
||||
pub struct Component {
|
||||
/// The base color of the widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub base: Srgba,
|
||||
/// The color of the widget when it is hovered
|
||||
#[serde(with = "color_serde")]
|
||||
pub hover: Srgba,
|
||||
/// the color of the widget when it is pressed
|
||||
#[serde(with = "color_serde")]
|
||||
pub pressed: Srgba,
|
||||
/// the color of the widget when it is selected
|
||||
#[serde(with = "color_serde")]
|
||||
pub selected: Srgba,
|
||||
/// the color of the widget when it is selected
|
||||
#[serde(with = "color_serde")]
|
||||
pub selected_text: Srgba,
|
||||
/// the color of the widget when it is focused
|
||||
#[serde(with = "color_serde")]
|
||||
pub focus: Srgba,
|
||||
/// the color of dividers for this widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub divider: Srgba,
|
||||
/// the color of text for this widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub on: Srgba,
|
||||
// the color of text with opacity 80 for this widget
|
||||
// pub text_opacity_80: Srgba,
|
||||
/// the color of the widget when it is disabled
|
||||
#[serde(with = "color_serde")]
|
||||
pub disabled: Srgba,
|
||||
/// the color of text in the widget when it is disabled
|
||||
#[serde(with = "color_serde")]
|
||||
pub on_disabled: Srgba,
|
||||
/// the color of the border for the widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub border: Srgba,
|
||||
/// the color of the border for the widget when it is disabled
|
||||
#[serde(with = "color_serde")]
|
||||
pub disabled_border: Srgba,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -1,13 +1,14 @@
|
|||
pub use corner::*;
|
||||
pub use cosmic_palette::*;
|
||||
pub use density::*;
|
||||
pub use derivation::*;
|
||||
pub use mode::*;
|
||||
pub use spacing::*;
|
||||
pub use theme::*;
|
||||
|
||||
pub mod color;
|
||||
mod corner;
|
||||
mod cosmic_palette;
|
||||
mod density;
|
||||
mod derivation;
|
||||
mod mode;
|
||||
mod spacing;
|
||||
|
|
|
|||
|
|
@ -41,59 +41,3 @@ impl Default for Spacing {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Density options for the Cosmic theme
|
||||
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Density {
|
||||
/// Lower padding/spacing of elements
|
||||
Compact,
|
||||
/// Standard padding/spacing of elements
|
||||
#[default]
|
||||
Standard,
|
||||
/// Higher padding/spacing of elements
|
||||
Spacious,
|
||||
}
|
||||
|
||||
impl From<Density> for Spacing {
|
||||
fn from(value: Density) -> Self {
|
||||
match value {
|
||||
Density::Compact => Spacing {
|
||||
space_none: 0,
|
||||
space_xxxs: 4,
|
||||
space_xxs: 4,
|
||||
space_xs: 8,
|
||||
space_s: 8,
|
||||
space_m: 16,
|
||||
space_l: 24,
|
||||
space_xl: 32,
|
||||
space_xxl: 48,
|
||||
space_xxxl: 64,
|
||||
},
|
||||
Density::Standard => Spacing::default(),
|
||||
Density::Spacious => Spacing {
|
||||
space_none: 4,
|
||||
space_xxxs: 8,
|
||||
space_xxs: 12,
|
||||
space_xs: 16,
|
||||
space_s: 24,
|
||||
space_m: 32,
|
||||
space_l: 48,
|
||||
space_xl: 64,
|
||||
space_xxl: 128,
|
||||
space_xxxl: 160,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Spacing> for Density {
|
||||
fn from(value: Spacing) -> Self {
|
||||
if value.space_m.saturating_sub(16) == 0 {
|
||||
Self::Compact
|
||||
} else if value.space_m.saturating_sub(24) == 0 {
|
||||
Self::Standard
|
||||
} else {
|
||||
Self::Spacious
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
use crate::color::color_serde::option as color_serde_option;
|
||||
use crate::color::{ColorRepr, ColorReprOption, color_serde};
|
||||
use crate::composite::over;
|
||||
use crate::steps::{color_index, get_small_widget_color, get_surface_color, get_text, steps};
|
||||
use crate::{
|
||||
Component, Container, CornerRadii, CosmicPalette, CosmicPaletteInner, DARK_PALETTE,
|
||||
LIGHT_PALETTE, NAME, Spacing, ThemeMode,
|
||||
composite::over,
|
||||
steps::{color_index, get_small_widget_color, get_surface_color, get_text, steps},
|
||||
};
|
||||
use cosmic_config::{Config, CosmicConfigEntry};
|
||||
use palette::color_difference::Wcag21RelativeContrast;
|
||||
use palette::rgb::Rgb;
|
||||
use palette::{IntoColor, Oklcha, Srgb, Srgba, WithAlpha};
|
||||
use palette::{
|
||||
IntoColor, Oklcha, Srgb, Srgba, WithAlpha, color_difference::Wcag21RelativeContrast, rgb::Rgb,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
|
|
@ -47,24 +45,16 @@ pub enum Layer {
|
|||
PartialEq,
|
||||
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||
)]
|
||||
#[version = 2]
|
||||
#[version = 1]
|
||||
pub struct Theme {
|
||||
/// name of the theme
|
||||
pub name: String,
|
||||
/// background element colors
|
||||
pub(crate) background: Container,
|
||||
pub background: Container,
|
||||
/// primary element colors
|
||||
pub(crate) primary: Container,
|
||||
pub primary: Container,
|
||||
/// secondary element colors
|
||||
pub(crate) secondary: Container,
|
||||
/// background element colors
|
||||
pub(crate) transparent_background: Container,
|
||||
/// primary element colors
|
||||
pub(crate) transparent_primary: Container,
|
||||
/// secondary element colors
|
||||
pub(crate) transparent_secondary: Container,
|
||||
/// button component styling
|
||||
pub button: Component,
|
||||
pub secondary: Container,
|
||||
/// accent element colors
|
||||
pub accent: Component,
|
||||
/// suggested element colors
|
||||
|
|
@ -85,10 +75,10 @@ pub struct Theme {
|
|||
pub icon_button: Component,
|
||||
/// link button element colors
|
||||
pub link_button: Component,
|
||||
/// list button element colors
|
||||
pub list_button: Component,
|
||||
/// text button element colors
|
||||
pub text_button: Component,
|
||||
/// button component styling
|
||||
pub button: Component,
|
||||
/// palette
|
||||
pub palette: CosmicPaletteInner,
|
||||
/// spacing
|
||||
|
|
@ -105,43 +95,16 @@ pub struct Theme {
|
|||
pub active_hint: u32,
|
||||
/// cosmic-comp custom window hint color
|
||||
pub window_hint: Option<Srgb>,
|
||||
#[serde(default)]
|
||||
/// enables blurred transparency
|
||||
pub frosted: BlurStrength,
|
||||
#[serde(default)]
|
||||
/// frosted windows
|
||||
pub frosted_windows: bool,
|
||||
#[serde(default)]
|
||||
/// frosted system interface
|
||||
pub frosted_system_interface: bool,
|
||||
#[serde(default)]
|
||||
/// frosted panel
|
||||
pub frosted_panel: bool,
|
||||
#[serde(default)]
|
||||
/// frosted applet popups
|
||||
pub frosted_applets: bool,
|
||||
#[serde(default)]
|
||||
/// frosted apps when maximized
|
||||
pub frosted_maximized_apps: bool,
|
||||
#[serde(default)]
|
||||
/// alpha map
|
||||
pub alpha_map: AlphaMap,
|
||||
pub is_frosted: bool,
|
||||
/// shade color for dialogs
|
||||
#[serde(with = "color_serde")]
|
||||
#[cosmic_config_entry(with = ColorRepr)]
|
||||
pub shade: Srgba,
|
||||
/// accent text colors
|
||||
/// If None, accent base color is the accent text color.
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub accent_text: Option<Srgba>,
|
||||
/// control tint color
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub control_tint: Option<Srgb>,
|
||||
/// text tint color
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub text_tint: Option<Srgb>,
|
||||
}
|
||||
|
||||
|
|
@ -207,39 +170,6 @@ impl Theme {
|
|||
todo!();
|
||||
}
|
||||
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
/// get opaque or transparent background based on whether blur is active
|
||||
pub fn background(&self, transparent: bool) -> &Container {
|
||||
if transparent {
|
||||
&self.transparent_background
|
||||
} else {
|
||||
&self.background
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
/// get opaque or transparent primary based on whether blur is active
|
||||
pub fn primary(&self, transparent: bool) -> &Container {
|
||||
if transparent {
|
||||
&self.transparent_primary
|
||||
} else {
|
||||
&self.primary
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
/// get opaque or transparent secondary based on whether blur is active
|
||||
pub fn secondary(&self, transparent: bool) -> &Container {
|
||||
if transparent {
|
||||
&self.transparent_secondary
|
||||
} else {
|
||||
&self.secondary
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
|
|
@ -809,7 +739,7 @@ impl Theme {
|
|||
if color_scheme.trim().contains("default") || color_scheme.trim().contains("light") {
|
||||
return Self::light_default();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Self::dark_default()
|
||||
}
|
||||
|
|
@ -818,10 +748,10 @@ impl Theme {
|
|||
pub fn preferred_theme() -> Self {
|
||||
let current_desktop = std::env::var("XDG_CURRENT_DESKTOP");
|
||||
|
||||
if let Ok(desktop) = current_desktop
|
||||
&& desktop.trim().to_lowercase().contains("gnome")
|
||||
{
|
||||
return Self::gtk_prefer_colorscheme();
|
||||
if let Ok(desktop) = current_desktop {
|
||||
if desktop.trim().to_lowercase().contains("gnome") {
|
||||
return Self::gtk_prefer_colorscheme();
|
||||
}
|
||||
}
|
||||
|
||||
Self::dark_default()
|
||||
|
|
@ -844,7 +774,7 @@ impl From<CosmicPalette> for Theme {
|
|||
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||
PartialEq,
|
||||
)]
|
||||
#[version = 2]
|
||||
#[version = 1]
|
||||
pub struct ThemeBuilder {
|
||||
/// override the palette for the builder
|
||||
pub palette: CosmicPalette,
|
||||
|
|
@ -853,70 +783,31 @@ pub struct ThemeBuilder {
|
|||
/// override corner radii for the builder
|
||||
pub corner_radii: CornerRadii,
|
||||
/// override neutral_tint for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub neutral_tint: Option<Srgb>,
|
||||
/// override bg_color for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub bg_color: Option<Srgba>,
|
||||
/// override the primary container bg color for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub primary_container_bg: Option<Srgba>,
|
||||
/// override the secontary container bg color for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub secondary_container_bg: Option<Srgba>,
|
||||
/// override the text tint for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub text_tint: Option<Srgb>,
|
||||
/// override the accent color for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub accent: Option<Srgb>,
|
||||
/// override the success color for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub success: Option<Srgb>,
|
||||
/// override the warning color for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub warning: Option<Srgb>,
|
||||
/// override the destructive color for the builder
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub destructive: Option<Srgb>,
|
||||
#[serde(default)]
|
||||
/// enabled blurred transparency
|
||||
pub frosted: BlurStrength,
|
||||
pub is_frosted: bool, // TODO handle
|
||||
/// cosmic-comp window gaps size (outer, inner)
|
||||
pub gaps: (u32, u32),
|
||||
/// cosmic-comp active hint window outline width
|
||||
pub active_hint: u32,
|
||||
/// cosmic-comp custom window hint color
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub window_hint: Option<Srgb>,
|
||||
#[serde(default)]
|
||||
/// frosted windows
|
||||
pub frosted_windows: bool,
|
||||
#[serde(default)]
|
||||
/// frosted system interface
|
||||
pub frosted_system_interface: bool,
|
||||
#[serde(default)]
|
||||
/// frosted panel
|
||||
pub frosted_panel: bool,
|
||||
#[serde(default)]
|
||||
/// frosted applet popups
|
||||
pub frosted_applets: bool,
|
||||
#[serde(default)]
|
||||
/// frosted apps when maximized
|
||||
pub frosted_maximized_apps: bool,
|
||||
#[serde(default)]
|
||||
/// alpha map
|
||||
pub alpha_map: AlphaMap,
|
||||
}
|
||||
|
||||
impl Default for ThemeBuilder {
|
||||
|
|
@ -934,17 +825,11 @@ impl Default for ThemeBuilder {
|
|||
success: Default::default(),
|
||||
warning: Default::default(),
|
||||
destructive: Default::default(),
|
||||
frosted: BlurStrength::default(),
|
||||
is_frosted: false,
|
||||
// cosmic-comp theme settings
|
||||
gaps: (0, 8),
|
||||
active_hint: 3,
|
||||
window_hint: None,
|
||||
frosted_windows: false,
|
||||
frosted_system_interface: false,
|
||||
frosted_panel: false,
|
||||
frosted_applets: false,
|
||||
frosted_maximized_apps: false,
|
||||
alpha_map: AlphaMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1068,12 +953,6 @@ impl ThemeBuilder {
|
|||
}
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
// The component_hovered/pressed_overlay vars are seeded once near the
|
||||
// top of this fn and then reassigned inside each container block
|
||||
// (primary, secondary, …) before being read again. The initial seed
|
||||
// is therefore overwritten before any read, which is what the
|
||||
// unused_assignments lint flags below.
|
||||
#[allow(unused_assignments)]
|
||||
/// build the theme
|
||||
pub fn build(self) -> Theme {
|
||||
let Self {
|
||||
|
|
@ -1092,23 +971,9 @@ impl ThemeBuilder {
|
|||
gaps,
|
||||
active_hint,
|
||||
window_hint,
|
||||
frosted,
|
||||
frosted_windows,
|
||||
frosted_system_interface,
|
||||
frosted_panel,
|
||||
frosted_applets,
|
||||
frosted_maximized_apps,
|
||||
alpha_map,
|
||||
is_frosted,
|
||||
} = self;
|
||||
|
||||
let container_alpha = alpha_map.blurred_alpha(frosted);
|
||||
let actual_alpha =
|
||||
if (frosted_windows || frosted_system_interface || frosted_panel || frosted_applets) {
|
||||
container_alpha
|
||||
} else {
|
||||
1.0
|
||||
};
|
||||
|
||||
let is_dark = palette.is_dark();
|
||||
let is_high_contrast = palette.is_high_contrast();
|
||||
|
||||
|
|
@ -1163,10 +1028,6 @@ impl ThemeBuilder {
|
|||
let step_array = steps(bg, NonZeroUsize::new(100).unwrap());
|
||||
let bg_index = color_index(bg, step_array.len());
|
||||
|
||||
let mut transparent_bg = bg;
|
||||
transparent_bg.alpha = container_alpha;
|
||||
let transparent_bg_steps_array = steps(transparent_bg, NonZeroUsize::new(100).unwrap());
|
||||
|
||||
let mut component_hovered_overlay = if bg_index < 91 {
|
||||
control_steps_array[10]
|
||||
} else {
|
||||
|
|
@ -1191,26 +1052,10 @@ impl ThemeBuilder {
|
|||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
);
|
||||
|
||||
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()),
|
||||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
);
|
||||
|
||||
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
|
||||
} else {
|
||||
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
|
||||
|
|
@ -1240,7 +1085,6 @@ impl ThemeBuilder {
|
|||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
container_alpha,
|
||||
),
|
||||
component_hovered_overlay,
|
||||
component_pressed_overlay,
|
||||
|
|
@ -1253,53 +1097,6 @@ impl ThemeBuilder {
|
|||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
container_alpha,
|
||||
),
|
||||
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||
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 t_step_array = steps(container_bg, NonZeroUsize::new(100).unwrap());
|
||||
let base_index: usize = color_index(container_bg, t_step_array.len());
|
||||
let component_base = get_surface_color(
|
||||
base_index,
|
||||
6,
|
||||
&t_step_array,
|
||||
is_dark,
|
||||
&control_steps_array[3],
|
||||
);
|
||||
|
||||
Container::new(
|
||||
Component::component(
|
||||
component_base,
|
||||
accent,
|
||||
get_text(
|
||||
color_index(component_base, t_step_array.len()),
|
||||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
),
|
||||
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(),
|
||||
actual_alpha,
|
||||
),
|
||||
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||
is_high_contrast,
|
||||
|
|
@ -1373,7 +1170,6 @@ impl ThemeBuilder {
|
|||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
),
|
||||
get_small_widget_color(bg_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||
is_high_contrast,
|
||||
|
|
@ -1386,15 +1182,10 @@ impl ThemeBuilder {
|
|||
get_surface_color(bg_index, 10, &step_array, is_dark, &control_steps_array[2])
|
||||
};
|
||||
|
||||
let t_step_array = steps(container_bg, NonZeroUsize::new(100).unwrap());
|
||||
let base_index = color_index(container_bg, t_step_array.len());
|
||||
let secondary_component = get_surface_color(
|
||||
base_index,
|
||||
3,
|
||||
&t_step_array,
|
||||
is_dark,
|
||||
&control_steps_array[4],
|
||||
);
|
||||
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]);
|
||||
|
||||
component_hovered_overlay = if base_index < 91 {
|
||||
control_steps_array[10]
|
||||
|
|
@ -1411,11 +1202,10 @@ impl ThemeBuilder {
|
|||
secondary_component,
|
||||
accent,
|
||||
get_text(
|
||||
color_index(secondary_component, t_step_array.len()),
|
||||
color_index(secondary_component, step_array.len()),
|
||||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
),
|
||||
component_hovered_overlay,
|
||||
component_pressed_overlay,
|
||||
|
|
@ -1428,7 +1218,6 @@ impl ThemeBuilder {
|
|||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
),
|
||||
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||
is_high_contrast,
|
||||
|
|
@ -1441,24 +1230,14 @@ impl ThemeBuilder {
|
|||
button_hovered_overlay,
|
||||
button_pressed_overlay,
|
||||
),
|
||||
accent_button: {
|
||||
let step_array = steps(control_steps_array[5], NonZeroUsize::new(100).unwrap());
|
||||
|
||||
Component::colored_button(
|
||||
accent,
|
||||
control_steps_array[1],
|
||||
get_text(
|
||||
color_index(accent, step_array.len()),
|
||||
&step_array,
|
||||
&step_array[0],
|
||||
text_steps_array.as_deref(),
|
||||
1., // accent is opaque anyway
|
||||
),
|
||||
accent,
|
||||
button_hovered_overlay,
|
||||
button_pressed_overlay,
|
||||
)
|
||||
},
|
||||
accent_button: Component::colored_button(
|
||||
accent,
|
||||
control_steps_array[1],
|
||||
control_steps_array[0],
|
||||
accent,
|
||||
button_hovered_overlay,
|
||||
button_pressed_overlay,
|
||||
),
|
||||
button: Component::component(
|
||||
button_bg,
|
||||
accent,
|
||||
|
|
@ -1506,15 +1285,6 @@ impl ThemeBuilder {
|
|||
component.on_disabled = over(component.on.with_alpha(0.5), component.base);
|
||||
component
|
||||
},
|
||||
list_button: Component::component(
|
||||
Srgba::new(0.0, 0.0, 0.0, 0.0),
|
||||
accent,
|
||||
on_bg_component,
|
||||
Srgba::new(0.0, 0.0, 0.0, 0.0),
|
||||
button_pressed_overlay,
|
||||
is_high_contrast,
|
||||
control_steps_array[8],
|
||||
),
|
||||
success: Component::colored_component(
|
||||
success,
|
||||
control_steps_array[0],
|
||||
|
|
@ -1562,80 +1332,10 @@ impl ThemeBuilder {
|
|||
gaps,
|
||||
active_hint,
|
||||
window_hint,
|
||||
frosted,
|
||||
is_frosted,
|
||||
accent_text,
|
||||
control_tint: neutral_tint,
|
||||
text_tint,
|
||||
frosted_windows,
|
||||
frosted_system_interface,
|
||||
frosted_panel,
|
||||
frosted_applets,
|
||||
frosted_maximized_apps,
|
||||
alpha_map,
|
||||
transparent_background: Container::new(
|
||||
Component::component(
|
||||
transparent_bg_component,
|
||||
accent,
|
||||
on_transparent_bg_component,
|
||||
component_hovered_overlay,
|
||||
component_pressed_overlay,
|
||||
is_high_contrast,
|
||||
control_steps_array[8],
|
||||
),
|
||||
transparent_bg,
|
||||
get_text(
|
||||
bg_index,
|
||||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
),
|
||||
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(),
|
||||
actual_alpha,
|
||||
),
|
||||
component_hovered_overlay,
|
||||
component_pressed_overlay,
|
||||
is_high_contrast,
|
||||
control_steps_array[8],
|
||||
),
|
||||
container_bg,
|
||||
get_text(
|
||||
base_index,
|
||||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
),
|
||||
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||
is_high_contrast,
|
||||
)
|
||||
},
|
||||
};
|
||||
theme.spacing = spacing;
|
||||
theme.corner_radii = corner_radii;
|
||||
|
|
@ -1654,181 +1354,3 @@ impl ThemeBuilder {
|
|||
Config::new(LIGHT_THEME_BUILDER_ID, Self::VERSION)
|
||||
}
|
||||
}
|
||||
|
||||
/// Actual blur radius is decided by cosmic-comp,
|
||||
/// but this represents the strength of the blur effect.
|
||||
#[allow(missing_docs)]
|
||||
#[repr(u8)]
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub enum BlurStrength {
|
||||
ExtremelyLow,
|
||||
ExtremelyLow2,
|
||||
VeryLow,
|
||||
VeryLow2,
|
||||
Low,
|
||||
Low2,
|
||||
Medium,
|
||||
Medium2,
|
||||
High,
|
||||
High2,
|
||||
VeryHigh,
|
||||
VeryHigh2,
|
||||
ExtremelyHigh,
|
||||
ExtremelyHigh2,
|
||||
}
|
||||
|
||||
impl Default for BlurStrength {
|
||||
fn default() -> Self {
|
||||
Self::Medium
|
||||
}
|
||||
}
|
||||
|
||||
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(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct AlphaMap {
|
||||
pub extremely_low: f32,
|
||||
pub extremely_low_2: f32,
|
||||
pub very_low: f32,
|
||||
pub very_low_2: f32,
|
||||
pub low: f32,
|
||||
pub low_2: f32,
|
||||
pub medium: f32,
|
||||
pub medium_2: f32,
|
||||
pub high: f32,
|
||||
pub high_2: f32,
|
||||
pub very_high: f32,
|
||||
pub very_high_2: f32,
|
||||
pub extremely_high: f32,
|
||||
pub extremely_high_2: f32,
|
||||
}
|
||||
|
||||
impl AlphaMap {
|
||||
pub fn blurred_alpha(&self, blur: BlurStrength) -> f32 {
|
||||
match blur {
|
||||
BlurStrength::ExtremelyLow => self.extremely_low,
|
||||
BlurStrength::ExtremelyLow2 => self.extremely_low_2,
|
||||
BlurStrength::VeryLow => self.very_low,
|
||||
BlurStrength::VeryLow2 => self.very_low_2,
|
||||
BlurStrength::Low => self.low,
|
||||
BlurStrength::Low2 => self.low_2,
|
||||
BlurStrength::Medium => self.medium,
|
||||
BlurStrength::Medium2 => self.medium_2,
|
||||
BlurStrength::High => self.high,
|
||||
BlurStrength::High2 => self.high_2,
|
||||
BlurStrength::VeryHigh => self.very_high,
|
||||
BlurStrength::VeryHigh2 => self.very_high_2,
|
||||
BlurStrength::ExtremelyHigh => self.extremely_high,
|
||||
BlurStrength::ExtremelyHigh2 => self.extremely_high_2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn offset(self, offset: f32) -> Self {
|
||||
let Self {
|
||||
mut extremely_low,
|
||||
mut extremely_low_2,
|
||||
mut very_low,
|
||||
mut very_low_2,
|
||||
mut low,
|
||||
mut low_2,
|
||||
mut medium,
|
||||
mut medium_2,
|
||||
mut high,
|
||||
mut high_2,
|
||||
mut very_high,
|
||||
mut very_high_2,
|
||||
mut extremely_high,
|
||||
mut extremely_high_2,
|
||||
} = self;
|
||||
extremely_low += offset;
|
||||
extremely_low_2 += offset;
|
||||
very_low += offset;
|
||||
very_low_2 += offset;
|
||||
low += offset;
|
||||
low_2 += offset;
|
||||
medium += offset;
|
||||
medium_2 += offset;
|
||||
high += offset;
|
||||
high_2 += offset;
|
||||
very_high += offset;
|
||||
very_high_2 += offset;
|
||||
extremely_high += offset;
|
||||
extremely_high_2 += offset;
|
||||
Self {
|
||||
extremely_low,
|
||||
extremely_low_2,
|
||||
very_low,
|
||||
very_low_2,
|
||||
low,
|
||||
low_2,
|
||||
medium,
|
||||
medium_2,
|
||||
high,
|
||||
high_2,
|
||||
very_high,
|
||||
very_high_2,
|
||||
extremely_high,
|
||||
extremely_high_2,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn guess_offset(&self) -> f32 {
|
||||
let Self {
|
||||
extremely_low,
|
||||
extremely_high_2,
|
||||
..
|
||||
} = self;
|
||||
let default_low = Self::default().extremely_low;
|
||||
let default_high = Self::default().extremely_high_2;
|
||||
if *extremely_low > default_low {
|
||||
0.5 * (*extremely_low - default_low) / (1.0 - default_low) + 0.5
|
||||
} else if *extremely_high_2 < default_high {
|
||||
0.5 - (0.5 * (default_high - *extremely_high_2) / default_high)
|
||||
} else {
|
||||
0.5
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AlphaMap {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
extremely_low: 0.90,
|
||||
extremely_low_2: 0.87692,
|
||||
very_low: 0.85385,
|
||||
very_low_2: 0.83076,
|
||||
low: 0.80769,
|
||||
low_2: 0.78461,
|
||||
medium: 0.76154,
|
||||
medium_2: 0.73846,
|
||||
high: 0.71538,
|
||||
high_2: 0.69231,
|
||||
very_high: 0.66023,
|
||||
very_high_2: 0.64615,
|
||||
extremely_high: 0.62308,
|
||||
extremely_high_2: 0.6,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,10 @@
|
|||
use crate::{Component, Theme, composite::over, steps::steps};
|
||||
use configparser::ini::Ini;
|
||||
use palette::{Darken, IntoColor, Lighten, Srgba, WithAlpha, rgb::Rgba};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{self, Write},
|
||||
num::NonZeroUsize,
|
||||
path::Path,
|
||||
process::Command,
|
||||
};
|
||||
|
||||
use super::{OutputError, to_rgba};
|
||||
|
|
@ -219,50 +217,6 @@ impl Theme {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply the preferred GTK client-side decoration button layout.
|
||||
///
|
||||
/// This writes the GTK 3/4 `settings.ini` value used by GTK header bars and
|
||||
/// also best-effort updates GNOME's `button-layout` GSettings key for apps
|
||||
/// that still consult it.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an `OutputError` if the GTK settings files cannot be written.
|
||||
#[cold]
|
||||
pub fn apply_gtk_decoration_layout(buttons_at_start: bool) -> Result<(), OutputError> {
|
||||
let Some(config_dir) = dirs::config_dir() else {
|
||||
return Err(OutputError::MissingConfigDir);
|
||||
};
|
||||
|
||||
let layout = if buttons_at_start {
|
||||
"close,minimize,maximize:"
|
||||
} else {
|
||||
":minimize,maximize,close"
|
||||
};
|
||||
|
||||
for gtk_version in ["gtk-3.0", "gtk-4.0"] {
|
||||
let gtk_dir = config_dir.join(gtk_version);
|
||||
fs::create_dir_all(>k_dir).map_err(OutputError::Io)?;
|
||||
Self::write_gtk_settings_key(
|
||||
>k_dir.join("settings.ini"),
|
||||
"gtk-decoration-layout",
|
||||
layout,
|
||||
)?;
|
||||
}
|
||||
|
||||
// best-effort: gsettings is absent on non-GNOME systems
|
||||
let _ = Command::new("gsettings")
|
||||
.args([
|
||||
"set",
|
||||
"org.gnome.desktop.wm.preferences",
|
||||
"button-layout",
|
||||
layout,
|
||||
])
|
||||
.status();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the applied gtk css
|
||||
///
|
||||
/// # Errors
|
||||
|
|
@ -302,20 +256,6 @@ impl Theme {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn write_gtk_settings_key(path: &Path, key: &str, value: &str) -> Result<(), OutputError> {
|
||||
let mut ini = Ini::new_cs();
|
||||
|
||||
if path.exists() {
|
||||
let file_content = fs::read_to_string(path).map_err(OutputError::Io)?;
|
||||
ini.read(file_content).map_err(OutputError::Ini)?;
|
||||
}
|
||||
|
||||
ini.setstr("Settings", key, Some(value));
|
||||
ini.pretty_write(path, &super::qt_settings_ini_style())
|
||||
.map_err(OutputError::Io)
|
||||
}
|
||||
|
||||
fn is_cosmic_css(path: &Path, cosmic_css: &Path) -> io::Result<Option<bool>> {
|
||||
if !path.exists() {
|
||||
return Ok(None);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
use configparser::ini::WriteOptions;
|
||||
use palette::Srgba;
|
||||
use palette::rgb::Rgba;
|
||||
use palette::{Srgba, rgb::Rgba};
|
||||
use thiserror::Error;
|
||||
|
||||
use crate::Theme;
|
||||
|
|
@ -34,8 +33,10 @@ impl Theme {
|
|||
/// Apply COSMIC theme exports for GTK and Qt applications.
|
||||
pub fn apply_exports(&self) -> Result<(), OutputError> {
|
||||
let gtk_res = Theme::apply_gtk(self.is_dark);
|
||||
let qt_res = Theme::apply_qt(self.is_dark);
|
||||
let qt56ct_res = Theme::apply_qt56ct(self.is_dark);
|
||||
gtk_res?;
|
||||
qt_res?;
|
||||
qt56ct_res?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -44,8 +45,10 @@ impl Theme {
|
|||
/// Write COSMIC theme exports for GTK and Qt applications.
|
||||
pub fn write_exports(&self) -> Result<(), OutputError> {
|
||||
let gtk_res = self.write_gtk4();
|
||||
let qt_res = self.write_qt();
|
||||
let qt56ct_res = self.write_qt56ct();
|
||||
gtk_res?;
|
||||
qt_res?;
|
||||
qt56ct_res?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
use crate::Theme;
|
||||
use configparser::ini::Ini;
|
||||
use palette::blend::Compose;
|
||||
use palette::rgb::Rgba;
|
||||
use palette::{Mix, Srgba, WithAlpha};
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
use std::vec;
|
||||
use palette::{Mix, Srgba, WithAlpha, blend::Compose, rgb::Rgba};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::Write,
|
||||
path::PathBuf,
|
||||
vec,
|
||||
};
|
||||
|
||||
use super::{OutputError, qt_settings_ini_style};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,281 @@
|
|||
use crate::Theme;
|
||||
use configparser::ini::Ini;
|
||||
use std::path::PathBuf;
|
||||
use std::{fs, io};
|
||||
use cosmic_config::CosmicConfigEntry;
|
||||
use palette::{Mix, Srgba, blend::Compose};
|
||||
use std::{
|
||||
fs::{self, File},
|
||||
io::{self, Write},
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use super::OutputError;
|
||||
use super::{OutputError, qt_settings_ini_style};
|
||||
|
||||
impl Theme {
|
||||
/// Reset the applied qt colors:
|
||||
/// - Remove relevant keys from the `~/.config/kdeglobals` file
|
||||
/// - Delete `~/.local/share/color-schemes/CosmicDark.colors` and `CosmicLight.colors`
|
||||
/// Produces a color scheme ini file for Qt.
|
||||
///
|
||||
/// Some high-level documentation for this file can be found at:
|
||||
/// - https://api.kde.org/kcolorscheme.html
|
||||
/// - https://web.archive.org/web/20250402234329/https://docs.kde.org/stable5/en/plasma-workspace/kcontrol/colors/
|
||||
#[must_use]
|
||||
#[cold]
|
||||
pub fn as_kcolorscheme(&self) -> String {
|
||||
// Usually, disabled elements will have strongly reduced contrast and are often notably darker or lighter
|
||||
let disabled_color_effects = IniColorEffects {
|
||||
color: self.button.disabled,
|
||||
color_amount: 0.0,
|
||||
color_effect: ColorEffect::Desaturate,
|
||||
contrast_amount: 0.65,
|
||||
contrast_effect: ColorEffect::Fade,
|
||||
intensity_amount: 0.1,
|
||||
intensity_effect: IntensityEffect::Lighten,
|
||||
};
|
||||
// Usually, inactive elements will have reduced contrast (text fades slightly into the background) and may have slightly reduced intensity
|
||||
let inactive_color_effects = IniColorEffects {
|
||||
color: self.palette.gray_1,
|
||||
color_amount: 0.025,
|
||||
color_effect: ColorEffect::Tint,
|
||||
contrast_amount: 0.1,
|
||||
contrast_effect: ColorEffect::Tint,
|
||||
intensity_amount: 0.0,
|
||||
intensity_effect: IntensityEffect::Shade,
|
||||
};
|
||||
|
||||
let bg = self.background.base;
|
||||
// the background container
|
||||
let window_colors = IniColors {
|
||||
background_alternate: bg.mix(self.accent.base, 0.05),
|
||||
background_normal: bg,
|
||||
decoration_focus: self.accent_text_color(),
|
||||
decoration_hover: self.accent_text_color(),
|
||||
foreground_active: self.accent_text_color(),
|
||||
foreground_inactive: self.background.on.mix(bg, 0.1),
|
||||
foreground_link: self.link_button.on,
|
||||
foreground_negative: self.destructive_text_color(),
|
||||
foreground_neutral: self.warning_text_color(),
|
||||
foreground_normal: self.background.on,
|
||||
foreground_positive: self.success_text_color(),
|
||||
foreground_visited: self.accent_text_color(),
|
||||
};
|
||||
// components inside the background container
|
||||
let view_colors = IniColors {
|
||||
background_alternate: self.background.component.base.mix(self.accent.base, 0.05),
|
||||
background_normal: self.background.component.base,
|
||||
..window_colors
|
||||
};
|
||||
|
||||
// selected text and items
|
||||
let selection_colors = {
|
||||
// selection colors are swapped to fix menu bar contrast
|
||||
let selected = self.background.component.selected_text;
|
||||
let selected_text = self.background.component.selected;
|
||||
IniColors {
|
||||
background_alternate: selected.mix(bg, 0.5),
|
||||
background_normal: selected,
|
||||
decoration_focus: selected,
|
||||
decoration_hover: selected,
|
||||
foreground_active: selected_text,
|
||||
foreground_inactive: selected_text.mix(selected, 0.5),
|
||||
foreground_link: self.link_button.base,
|
||||
foreground_negative: self.destructive_color(),
|
||||
foreground_neutral: self.warning_color(),
|
||||
foreground_normal: selected_text,
|
||||
foreground_positive: self.success_color(),
|
||||
foreground_visited: self.accent_color(),
|
||||
}
|
||||
};
|
||||
|
||||
let button_colors = IniColors {
|
||||
background_alternate: self.accent_button.base,
|
||||
background_normal: self.button.base,
|
||||
..view_colors
|
||||
};
|
||||
|
||||
// Complementary: Areas of applications with an alternative color scheme; usually with a dark background for light color schemes.
|
||||
let complementary_colors = {
|
||||
let dark = if self.is_dark {
|
||||
self.clone()
|
||||
} else if cfg!(test) {
|
||||
// For reproducible results in tests, use the default dark theme
|
||||
Theme::dark_default()
|
||||
} else {
|
||||
Theme::dark_config()
|
||||
.ok()
|
||||
.as_ref()
|
||||
.and_then(|conf| Theme::get_entry(conf).ok())
|
||||
.unwrap_or_else(|| self.clone())
|
||||
};
|
||||
IniColors {
|
||||
background_alternate: dark.accent.base,
|
||||
background_normal: dark.background.base,
|
||||
decoration_focus: dark.accent_text_color(),
|
||||
decoration_hover: dark.accent_text_color(),
|
||||
foreground_active: dark.accent_text_color(),
|
||||
foreground_inactive: dark.background.on.mix(dark.background.base, 0.1),
|
||||
foreground_link: dark.link_button.on,
|
||||
foreground_negative: dark.destructive_text_color(),
|
||||
foreground_neutral: dark.warning_text_color(),
|
||||
foreground_normal: dark.background.on,
|
||||
foreground_positive: dark.success_text_color(),
|
||||
foreground_visited: dark.accent_text_color(),
|
||||
}
|
||||
};
|
||||
|
||||
// headers in cosmic don't have a background
|
||||
let header_colors = &window_colors;
|
||||
let header_colors_inactive = &window_colors;
|
||||
// tool tips, "What's This" tips, and similar elements
|
||||
let tooltip_colors = &view_colors;
|
||||
|
||||
let general_color_scheme = if self.is_dark {
|
||||
"CosmicDark"
|
||||
} else {
|
||||
"CosmicLight"
|
||||
};
|
||||
let general_name = if self.is_dark {
|
||||
"COSMIC Dark"
|
||||
} else {
|
||||
"COSMIC Light"
|
||||
};
|
||||
// COSMIC icons are stuck in light mode, so use breeze icons instead
|
||||
let icons_theme = if self.is_dark {
|
||||
"breeze-dark"
|
||||
} else {
|
||||
"breeze"
|
||||
};
|
||||
|
||||
format!(
|
||||
r#"# GENERATED BY COSMIC
|
||||
|
||||
[ColorEffects:Disabled]
|
||||
{}
|
||||
|
||||
[ColorEffects:Inactive]
|
||||
ChangeSelectionColor=false
|
||||
Enable=false
|
||||
{}
|
||||
|
||||
[Colors:Button]
|
||||
{}
|
||||
|
||||
[Colors:Complementary]
|
||||
{}
|
||||
|
||||
[Colors:Header]
|
||||
{}
|
||||
|
||||
[Colors:Header][Inactive]
|
||||
{}
|
||||
|
||||
[Colors:Selection]
|
||||
{}
|
||||
|
||||
[Colors:Tooltip]
|
||||
{}
|
||||
|
||||
[Colors:View]
|
||||
{}
|
||||
|
||||
[Colors:Window]
|
||||
{}
|
||||
|
||||
[General]
|
||||
ColorScheme={general_color_scheme}
|
||||
Name={general_name}
|
||||
shadeSortColumn=true
|
||||
|
||||
[Icons]
|
||||
Theme={icons_theme}
|
||||
|
||||
[KDE]
|
||||
contrast=4
|
||||
widgetStyle=qt6ct-style
|
||||
|
||||
[WM]
|
||||
{}
|
||||
"#,
|
||||
format_ini_color_effects(&disabled_color_effects, bg),
|
||||
format_ini_color_effects(&inactive_color_effects, bg),
|
||||
format_ini_colors(&button_colors, bg),
|
||||
format_ini_colors(&complementary_colors, bg),
|
||||
format_ini_colors(&header_colors, bg),
|
||||
format_ini_colors(&header_colors_inactive, bg),
|
||||
format_ini_colors(&selection_colors, bg),
|
||||
format_ini_colors(&tooltip_colors, bg),
|
||||
format_ini_colors(&view_colors, bg),
|
||||
format_ini_colors(&window_colors, bg),
|
||||
format_ini_wm_colors(&window_colors, self.is_dark),
|
||||
)
|
||||
}
|
||||
|
||||
/// Write the color scheme to the appropriate directory.
|
||||
/// Should be written in `~/.local/share/color-schemes/`.
|
||||
///
|
||||
/// See the docs: https://develop.kde.org/docs/plasma/#color-scheme
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an `OutputError` if there is an error writing the colors file.
|
||||
#[cold]
|
||||
pub fn write_qt(&self) -> Result<(), OutputError> {
|
||||
let kcolorscheme = self.as_kcolorscheme();
|
||||
let file_path = Self::get_kcolorscheme_path(self.is_dark)?;
|
||||
let tmp_file_path = file_path.with_extension("colors.new");
|
||||
|
||||
// Write to tmp_file_path first, then move it to file_path
|
||||
let mut tmp_file = File::create(&tmp_file_path).map_err(OutputError::Io)?;
|
||||
let res = tmp_file
|
||||
.write_all(kcolorscheme.as_bytes())
|
||||
.and_then(|_| tmp_file.flush())
|
||||
.and_then(|_| std::fs::rename(&tmp_file_path, file_path));
|
||||
if let Err(e) = res {
|
||||
_ = std::fs::remove_file(&tmp_file_path);
|
||||
return Err(OutputError::Io(e));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Apply the color scheme by copying its values to `~/.config/kdeglobals`.
|
||||
///
|
||||
/// See the docs: https://develop.kde.org/docs/plasma/#color-scheme
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an `OutputError` if there is an error applying the color scheme.
|
||||
#[cold]
|
||||
pub fn apply_qt(is_dark: bool) -> Result<(), OutputError> {
|
||||
let Some(config_dir) = dirs::config_dir() else {
|
||||
return Err(OutputError::MissingConfigDir);
|
||||
};
|
||||
let kdeglobals_file = config_dir.join("kdeglobals");
|
||||
let mut kdeglobals_ini = Self::read_ini(&kdeglobals_file)?;
|
||||
|
||||
let src_file = Self::get_kcolorscheme_path(is_dark)?;
|
||||
let src_ini = Self::read_ini(&src_file)?;
|
||||
|
||||
Self::backup_non_cosmic_kdeglobals(&kdeglobals_ini, &kdeglobals_file)
|
||||
.map_err(OutputError::Io)?;
|
||||
|
||||
for (section, key_value) in src_ini.get_map_ref() {
|
||||
for (key, value) in key_value {
|
||||
kdeglobals_ini.set(section, key, value.clone());
|
||||
}
|
||||
}
|
||||
|
||||
kdeglobals_ini
|
||||
.pretty_write(kdeglobals_file, &qt_settings_ini_style())
|
||||
.map_err(OutputError::Io)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Reset the applied qt colors by removing color scheme values from the
|
||||
/// `~/.config/kdeglobals` file.
|
||||
///
|
||||
/// This does not restore the backed up kdeglobals file.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an `OutputError` if there is an error resetting the kdeglobals
|
||||
/// file or deleting the color schemes.
|
||||
/// Returns an `OutputError` if there is an error resetting the CSS file.
|
||||
#[cold]
|
||||
pub fn reset_qt() -> Result<(), OutputError> {
|
||||
let Some(config_dir) = dirs::config_dir() else {
|
||||
|
|
@ -32,29 +292,19 @@ impl Theme {
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
let light_scheme = Self::get_kcolorscheme_path(false)?;
|
||||
let dark_scheme = Self::get_kcolorscheme_path(true)?;
|
||||
if light_scheme.exists() {
|
||||
let src_ini = Self::read_ini(&light_scheme)?;
|
||||
let is_dark = false; // doesn't matter since we're only reading keys
|
||||
let src_file = Self::get_kcolorscheme_path(is_dark)?;
|
||||
let src_ini = Self::read_ini(&src_file)?;
|
||||
|
||||
// Remove color scheme keys from kdeglobals
|
||||
for (section, key_value) in src_ini.get_map_ref() {
|
||||
for key in key_value.keys() {
|
||||
kdeglobals_ini.remove_key(section, key);
|
||||
}
|
||||
for (section, key_value) in src_ini.get_map_ref() {
|
||||
for (key, _) in key_value {
|
||||
kdeglobals_ini.remove_key(section, key);
|
||||
}
|
||||
|
||||
kdeglobals_ini
|
||||
.write(kdeglobals_file)
|
||||
.map_err(OutputError::Io)?;
|
||||
}
|
||||
|
||||
// Delete now-unused kcolorscheme files
|
||||
let delete_light_res = fs::remove_file(&light_scheme).map_err(OutputError::Io);
|
||||
let delete_dark_res = fs::remove_file(&dark_scheme).map_err(OutputError::Io);
|
||||
delete_light_res?;
|
||||
delete_dark_res?;
|
||||
|
||||
kdeglobals_ini
|
||||
.write(kdeglobals_file)
|
||||
.map_err(OutputError::Io)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -63,7 +313,6 @@ impl Theme {
|
|||
let Some(mut data_dir) = dirs::data_dir() else {
|
||||
return Err(OutputError::MissingDataDir);
|
||||
};
|
||||
data_dir.push("color-schemes");
|
||||
|
||||
let file_name = if is_dark {
|
||||
"CosmicDark.colors"
|
||||
|
|
@ -71,6 +320,11 @@ impl Theme {
|
|||
"CosmicLight.colors"
|
||||
};
|
||||
|
||||
data_dir.push("color-schemes");
|
||||
if !data_dir.exists() {
|
||||
std::fs::create_dir_all(&data_dir).map_err(OutputError::Io)?;
|
||||
}
|
||||
|
||||
Ok(data_dir.join(file_name))
|
||||
}
|
||||
|
||||
|
|
@ -85,6 +339,15 @@ impl Theme {
|
|||
Ok(ini)
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn backup_non_cosmic_kdeglobals(ini: &Ini, path: &Path) -> io::Result<()> {
|
||||
if !Self::is_cosmic_kdeglobals(&ini)?.unwrap_or(true) {
|
||||
let backup_path = path.with_extension("bak");
|
||||
fs::copy(path, &backup_path)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cold]
|
||||
fn is_cosmic_kdeglobals(ini: &Ini) -> io::Result<Option<bool>> {
|
||||
let color_scheme = ini.get("General", "ColorScheme");
|
||||
|
|
@ -98,43 +361,208 @@ impl Theme {
|
|||
}
|
||||
}
|
||||
|
||||
/// Formats a color in the form `r,g,b` e.g. `255,255,255`.
|
||||
/// If the color has transparency, it is mixed with bg first.
|
||||
fn to_rgb(c: Srgba, bg: Srgba) -> String {
|
||||
let c_u8: Srgba<u8> = c.over(bg).into_format();
|
||||
format!("{},{},{}", c_u8.red, c_u8.green, c_u8.blue)
|
||||
}
|
||||
|
||||
fn format_ini_color_effects(color_effects: &IniColorEffects, bg: Srgba) -> String {
|
||||
format!(
|
||||
r#"Color={}
|
||||
ColorAmount={}
|
||||
ColorEffect={}
|
||||
ContrastAmount={}
|
||||
ContrastEffect={}
|
||||
IntensityAmount={}
|
||||
IntensityEffect={}"#,
|
||||
to_rgb(color_effects.color, bg),
|
||||
color_effects.color_amount,
|
||||
color_effects.color_effect.as_u8(),
|
||||
color_effects.contrast_amount,
|
||||
color_effects.contrast_effect.as_u8(),
|
||||
color_effects.intensity_amount,
|
||||
color_effects.intensity_effect.as_u8(),
|
||||
)
|
||||
}
|
||||
|
||||
fn format_ini_colors(colors: &IniColors, bg: Srgba) -> String {
|
||||
format!(
|
||||
r#"BackgroundAlternate={}
|
||||
BackgroundNormal={}
|
||||
DecorationFocus={}
|
||||
DecorationHover={}
|
||||
ForegroundActive={}
|
||||
ForegroundInactive={}
|
||||
ForegroundLink={}
|
||||
ForegroundNegative={}
|
||||
ForegroundNeutral={}
|
||||
ForegroundNormal={}
|
||||
ForegroundPositive={}
|
||||
ForegroundVisited={}"#,
|
||||
to_rgb(colors.background_alternate, bg),
|
||||
to_rgb(colors.background_normal, bg),
|
||||
to_rgb(colors.decoration_focus, bg),
|
||||
to_rgb(colors.decoration_hover, bg),
|
||||
to_rgb(colors.foreground_active, bg),
|
||||
to_rgb(colors.foreground_inactive, bg),
|
||||
to_rgb(colors.foreground_link, bg),
|
||||
to_rgb(colors.foreground_negative, bg),
|
||||
to_rgb(colors.foreground_neutral, bg),
|
||||
to_rgb(colors.foreground_normal, bg),
|
||||
to_rgb(colors.foreground_positive, bg),
|
||||
to_rgb(colors.foreground_visited, bg),
|
||||
)
|
||||
}
|
||||
|
||||
/// Sets the colors for the titlebars of active and inactive windows.
|
||||
fn format_ini_wm_colors(view_colors: &IniColors, is_dark: bool) -> String {
|
||||
let bg = view_colors.background_normal;
|
||||
let fg = view_colors.foreground_active;
|
||||
let blend = if is_dark { fg } else { bg };
|
||||
|
||||
format!(
|
||||
r#"activeBackground={}
|
||||
activeBlend={}
|
||||
activeForeground={}
|
||||
inactiveBackground={}
|
||||
inactiveBlend={}
|
||||
inactiveForeground={}"#,
|
||||
to_rgb(bg, bg),
|
||||
to_rgb(blend, bg),
|
||||
to_rgb(fg, bg),
|
||||
to_rgb(bg, bg),
|
||||
to_rgb(blend, bg),
|
||||
to_rgb(fg, bg),
|
||||
)
|
||||
}
|
||||
|
||||
struct IniColorEffects {
|
||||
color: Srgba,
|
||||
color_amount: f32,
|
||||
color_effect: ColorEffect,
|
||||
contrast_amount: f32,
|
||||
/// Applied to the text, using the background as the reference color.
|
||||
contrast_effect: ColorEffect,
|
||||
intensity_amount: f32,
|
||||
intensity_effect: IntensityEffect,
|
||||
}
|
||||
/// Each color set is made up of a number of roles which are available in all other sets.
|
||||
/// In addition, except for Inactive Text, there is a corresponding background role for each of the text roles. Currently (except for Normal and Alternate Background), these colors are not chosen here but are automatically determined based on Normal Background and the corresponding Text color.
|
||||
struct IniColors {
|
||||
/// used when there is a need to subtly change the background to aid in item association. This might be used e.g. as the background of a heading, but is mostly used for alternating rows in lists, especially multi-column lists, to aid in visually tracking rows.
|
||||
background_alternate: Srgba,
|
||||
/// Normal background
|
||||
background_normal: Srgba,
|
||||
/// Used for drawing lines or shading UI elements to indicate the item which has active input focus.
|
||||
/// Typically the same as foreground_active.
|
||||
decoration_focus: Srgba,
|
||||
/// Used for drawing lines or shading UI elements for mouse-over effects, e.g. the "illumination" effects for buttons.
|
||||
/// Typically the same as foreground_active.
|
||||
decoration_hover: Srgba,
|
||||
/// used to indicate an active element or attract attention, e.g. alerts, notifications; also for hovered hyperlinks
|
||||
foreground_active: Srgba,
|
||||
/// used for text which should be unobtrusive, e.g. comments, "subtitles", unimportant information, etc.
|
||||
foreground_inactive: Srgba,
|
||||
/// used for hyperlinks or to otherwise indicate "something which may be visited", or to show relationships
|
||||
foreground_link: Srgba,
|
||||
/// used for errors, failure notices, notifications that an action may be dangerous (e.g. unsafe web page or security context), etc.
|
||||
foreground_negative: Srgba,
|
||||
/// used to draw attention when another role is not appropriate; e.g. warnings, to indicate secure/encrypted content, etc.
|
||||
foreground_neutral: Srgba,
|
||||
/// Normal foreground
|
||||
foreground_normal: Srgba,
|
||||
/// used for success notices, to indicate trusted content, etc.
|
||||
foreground_positive: Srgba,
|
||||
/// used for "something (e.g. a hyperlink) that has been visited", or to indicate something that is "old".
|
||||
foreground_visited: Srgba,
|
||||
}
|
||||
|
||||
/// Intensity allows the overall color to be lightened or darkened.
|
||||
#[allow(dead_code)]
|
||||
enum IntensityEffect {
|
||||
/// Makes everything lighter or darker in a controlled manner.
|
||||
///
|
||||
/// intensity_amount increases or decreases the overall intensity (i.e. perceived brightness) by an absolute amount.
|
||||
Shade,
|
||||
/// Changes the intensity to a percentage of the initial value.
|
||||
Darken,
|
||||
/// Conceptually the opposite of darken; lighten can be thought of as working with "distance from white", where darken works with "distance from black".
|
||||
Lighten,
|
||||
}
|
||||
|
||||
impl IntensityEffect {
|
||||
pub fn as_u8(&self) -> u8 {
|
||||
match self {
|
||||
Self::Shade => 0,
|
||||
Self::Darken => 1,
|
||||
Self::Lighten => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// This also changes the overall color like [IntensityEffect],
|
||||
/// but is not limited to intensity.
|
||||
#[allow(dead_code)]
|
||||
enum ColorEffect {
|
||||
/// changes the relative chroma
|
||||
///
|
||||
/// This is available for "ColorEffect" but not "ContrastEffect".
|
||||
Desaturate,
|
||||
/// smoothly blends the original color into a reference color
|
||||
Fade,
|
||||
/// similar to Fade, except that the color (hue and chroma) changes more quickly while the intensity changes more slowly as the amount is increased
|
||||
Tint,
|
||||
}
|
||||
|
||||
impl ColorEffect {
|
||||
pub fn as_u8(&self) -> u8 {
|
||||
match self {
|
||||
Self::Desaturate => 0,
|
||||
Self::Fade => 1,
|
||||
Self::Tint => 2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_is_cosmic_kdeglobals_dark() -> io::Result<()> {
|
||||
let mut ini = Ini::new_cs();
|
||||
ini.setstr("General", "ColorScheme", Some("CosmicDark"));
|
||||
let is_cosmic = Theme::is_cosmic_kdeglobals(&ini)?;
|
||||
assert_eq!(is_cosmic, Some(true));
|
||||
Ok(())
|
||||
fn test_opaque_color_to_rgb() {
|
||||
let color = Srgba::new(30.0 / 255.0, 50.0 / 255.0, 70.0 / 255.0, 1.0);
|
||||
let bg = Srgba::new(1.0, 1.0, 1.0, 1.0);
|
||||
let result = to_rgb(color, bg);
|
||||
assert_eq!(result, "30,50,70");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_cosmic_kdeglobals_light() -> io::Result<()> {
|
||||
let mut ini = Ini::new_cs();
|
||||
ini.setstr("General", "ColorScheme", Some("CosmicLight"));
|
||||
let is_cosmic = Theme::is_cosmic_kdeglobals(&ini)?;
|
||||
assert_eq!(is_cosmic, Some(true));
|
||||
Ok(())
|
||||
fn test_transparent_color_to_rgb() {
|
||||
let color = Srgba::new(0.0, 0.0, 0.0, 0.0);
|
||||
let bg = Srgba::new(1.0, 1.0, 1.0, 1.0);
|
||||
let result = to_rgb(color, bg);
|
||||
assert_eq!(result, "255,255,255");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_cosmic_kdeglobals_breeze() -> io::Result<()> {
|
||||
let mut ini = Ini::new_cs();
|
||||
ini.setstr("General", "ColorScheme", Some("BreezeDark"));
|
||||
let is_cosmic = Theme::is_cosmic_kdeglobals(&ini)?;
|
||||
assert_eq!(is_cosmic, Some(false));
|
||||
Ok(())
|
||||
fn test_translucent_color_to_rgb() {
|
||||
let color = Srgba::new(0.0, 0.0, 0.0, 0.9);
|
||||
let bg = Srgba::new(1.0, 1.0, 1.0, 1.0);
|
||||
let result = to_rgb(color, bg);
|
||||
assert_eq!(result, "26,26,26");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_cosmic_kdeglobals_blank() -> io::Result<()> {
|
||||
let mut ini = Ini::new_cs();
|
||||
ini.setstr("General", "ColorScheme", None);
|
||||
let is_cosmic = Theme::is_cosmic_kdeglobals(&ini)?;
|
||||
assert_eq!(is_cosmic, None);
|
||||
Ok(())
|
||||
fn test_light_default_kcolorscheme() {
|
||||
let light_default_kcolorscheme = Theme::light_default().as_kcolorscheme();
|
||||
insta::assert_snapshot!(light_default_kcolorscheme);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dark_default_kcolorscheme() {
|
||||
let dark_default_kcolorscheme = Theme::dark_default().as_kcolorscheme();
|
||||
insta::assert_snapshot!(dark_default_kcolorscheme);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
source: cosmic-theme/src/output/qt_output.rs
|
||||
expression: dark_default_kcolorscheme
|
||||
---
|
||||
# GENERATED BY COSMIC
|
||||
|
||||
[ColorEffects:Disabled]
|
||||
Color=43,43,43
|
||||
ColorAmount=0
|
||||
ColorEffect=0
|
||||
ContrastAmount=0.65
|
||||
ContrastEffect=1
|
||||
IntensityAmount=0.1
|
||||
IntensityEffect=2
|
||||
|
||||
[ColorEffects:Inactive]
|
||||
ChangeSelectionColor=false
|
||||
Enable=false
|
||||
Color=27,27,27
|
||||
ColorAmount=0.025
|
||||
ColorEffect=2
|
||||
ContrastAmount=0.1
|
||||
ContrastEffect=2
|
||||
IntensityAmount=0
|
||||
IntensityEffect=0
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundAlternate=99,208,223
|
||||
BackgroundNormal=60,60,60
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate=99,208,223
|
||||
BackgroundNormal=27,27,27
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundAlternate=31,36,37
|
||||
BackgroundNormal=27,27,27
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:Header][Inactive]
|
||||
BackgroundAlternate=31,36,37
|
||||
BackgroundNormal=27,27,27
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate=63,118,125
|
||||
BackgroundNormal=99,208,223
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=67,67,67
|
||||
ForegroundInactive=83,138,145
|
||||
ForegroundLink=27,27,27
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=67,67,67
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate=49,55,55
|
||||
BackgroundNormal=46,46,46
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:View]
|
||||
BackgroundAlternate=49,55,55
|
||||
BackgroundNormal=46,46,46
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundAlternate=31,36,37
|
||||
BackgroundNormal=27,27,27
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[General]
|
||||
ColorScheme=CosmicDark
|
||||
Name=COSMIC Dark
|
||||
shadeSortColumn=true
|
||||
|
||||
[Icons]
|
||||
Theme=breeze-dark
|
||||
|
||||
[KDE]
|
||||
contrast=4
|
||||
widgetStyle=qt6ct-style
|
||||
|
||||
[WM]
|
||||
activeBackground=27,27,27
|
||||
activeBlend=99,208,223
|
||||
activeForeground=99,208,223
|
||||
inactiveBackground=27,27,27
|
||||
inactiveBlend=99,208,223
|
||||
inactiveForeground=99,208,223
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
---
|
||||
source: cosmic-theme/src/output/qt_output.rs
|
||||
expression: light_default_kcolorscheme
|
||||
---
|
||||
# GENERATED BY COSMIC
|
||||
|
||||
[ColorEffects:Disabled]
|
||||
Color=194,194,194
|
||||
ColorAmount=0
|
||||
ColorEffect=0
|
||||
ContrastAmount=0.65
|
||||
ContrastEffect=1
|
||||
IntensityAmount=0.1
|
||||
IntensityEffect=2
|
||||
|
||||
[ColorEffects:Inactive]
|
||||
ChangeSelectionColor=false
|
||||
Enable=false
|
||||
Color=215,215,215
|
||||
ColorAmount=0.025
|
||||
ColorEffect=2
|
||||
ContrastAmount=0.1
|
||||
ContrastEffect=2
|
||||
IntensityAmount=0
|
||||
IntensityEffect=0
|
||||
|
||||
[Colors:Button]
|
||||
BackgroundAlternate=0,82,90
|
||||
BackgroundNormal=173,173,173
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
ForegroundActive=0,82,90
|
||||
ForegroundInactive=38,38,38
|
||||
ForegroundLink=0,82,90
|
||||
ForegroundNegative=137,4,24
|
||||
ForegroundNeutral=121,44,0
|
||||
ForegroundNormal=18,18,18
|
||||
ForegroundPositive=0,87,44
|
||||
ForegroundVisited=0,82,90
|
||||
|
||||
[Colors:Complementary]
|
||||
BackgroundAlternate=99,208,223
|
||||
BackgroundNormal=27,27,27
|
||||
DecorationFocus=99,208,223
|
||||
DecorationHover=99,208,223
|
||||
ForegroundActive=99,208,223
|
||||
ForegroundInactive=211,211,211
|
||||
ForegroundLink=99,208,223
|
||||
ForegroundNegative=255,160,154
|
||||
ForegroundNeutral=255,163,125
|
||||
ForegroundNormal=231,231,231
|
||||
ForegroundPositive=94,219,140
|
||||
ForegroundVisited=99,208,223
|
||||
|
||||
[Colors:Header]
|
||||
BackgroundAlternate=204,208,209
|
||||
BackgroundNormal=215,215,215
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
ForegroundActive=0,82,90
|
||||
ForegroundInactive=38,38,38
|
||||
ForegroundLink=0,82,90
|
||||
ForegroundNegative=137,4,24
|
||||
ForegroundNeutral=121,44,0
|
||||
ForegroundNormal=18,18,18
|
||||
ForegroundPositive=0,87,44
|
||||
ForegroundVisited=0,82,90
|
||||
|
||||
[Colors:Header][Inactive]
|
||||
BackgroundAlternate=204,208,209
|
||||
BackgroundNormal=215,215,215
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
ForegroundActive=0,82,90
|
||||
ForegroundInactive=38,38,38
|
||||
ForegroundLink=0,82,90
|
||||
ForegroundNegative=137,4,24
|
||||
ForegroundNeutral=121,44,0
|
||||
ForegroundNormal=18,18,18
|
||||
ForegroundPositive=0,87,44
|
||||
ForegroundVisited=0,82,90
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate=108,149,152
|
||||
BackgroundNormal=0,82,90
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
ForegroundActive=246,246,246
|
||||
ForegroundInactive=123,164,168
|
||||
ForegroundLink=215,215,215
|
||||
ForegroundNegative=137,4,24
|
||||
ForegroundNeutral=121,44,0
|
||||
ForegroundNormal=246,246,246
|
||||
ForegroundPositive=0,87,44
|
||||
ForegroundVisited=0,82,90
|
||||
|
||||
[Colors:Tooltip]
|
||||
BackgroundAlternate=233,237,237
|
||||
BackgroundNormal=245,245,245
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
ForegroundActive=0,82,90
|
||||
ForegroundInactive=38,38,38
|
||||
ForegroundLink=0,82,90
|
||||
ForegroundNegative=137,4,24
|
||||
ForegroundNeutral=121,44,0
|
||||
ForegroundNormal=18,18,18
|
||||
ForegroundPositive=0,87,44
|
||||
ForegroundVisited=0,82,90
|
||||
|
||||
[Colors:View]
|
||||
BackgroundAlternate=233,237,237
|
||||
BackgroundNormal=245,245,245
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
ForegroundActive=0,82,90
|
||||
ForegroundInactive=38,38,38
|
||||
ForegroundLink=0,82,90
|
||||
ForegroundNegative=137,4,24
|
||||
ForegroundNeutral=121,44,0
|
||||
ForegroundNormal=18,18,18
|
||||
ForegroundPositive=0,87,44
|
||||
ForegroundVisited=0,82,90
|
||||
|
||||
[Colors:Window]
|
||||
BackgroundAlternate=204,208,209
|
||||
BackgroundNormal=215,215,215
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
ForegroundActive=0,82,90
|
||||
ForegroundInactive=38,38,38
|
||||
ForegroundLink=0,82,90
|
||||
ForegroundNegative=137,4,24
|
||||
ForegroundNeutral=121,44,0
|
||||
ForegroundNormal=18,18,18
|
||||
ForegroundPositive=0,87,44
|
||||
ForegroundVisited=0,82,90
|
||||
|
||||
[General]
|
||||
ColorScheme=CosmicLight
|
||||
Name=COSMIC Light
|
||||
shadeSortColumn=true
|
||||
|
||||
[Icons]
|
||||
Theme=breeze
|
||||
|
||||
[KDE]
|
||||
contrast=4
|
||||
widgetStyle=qt6ct-style
|
||||
|
||||
[WM]
|
||||
activeBackground=215,215,215
|
||||
activeBlend=215,215,215
|
||||
activeForeground=0,82,90
|
||||
inactiveBackground=215,215,215
|
||||
inactiveBlend=215,215,215
|
||||
inactiveForeground=0,82,90
|
||||
|
|
@ -266,14 +266,6 @@ impl From<Theme> for VsTheme {
|
|||
}
|
||||
|
||||
impl Theme {
|
||||
/// Write this theme to VS Code's `settings.json` as a
|
||||
/// `workbench.colorCustomizations` entry, and enable
|
||||
/// `window.autoDetectColorScheme` so VS Code follows the system theme.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an `OutputError` if the user config dir is missing, the
|
||||
/// settings file cannot be read/written, or its JSON is invalid.
|
||||
#[cold]
|
||||
pub fn apply_vs_code(self) -> Result<(), OutputError> {
|
||||
let vs_theme = VsTheme::from(self);
|
||||
|
|
@ -299,13 +291,6 @@ impl Theme {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// Remove the `workbench.colorCustomizations` entry previously written
|
||||
/// by [`Theme::apply_vs_code`] from VS Code's `settings.json`.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns an `OutputError` if the user config dir is missing, the
|
||||
/// settings file cannot be read/written, or its JSON is invalid.
|
||||
#[cold]
|
||||
pub fn reset_vs_code() -> Result<(), OutputError> {
|
||||
let mut config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,7 @@
|
|||
use std::num::NonZeroUsize;
|
||||
|
||||
use almost::equal;
|
||||
use palette::convert::FromColorUnclamped;
|
||||
use palette::{ClampAssign, FromColor, Lch, Oklcha, Srgb, Srgba};
|
||||
use palette::{ClampAssign, FromColor, Lch, Oklcha, Srgb, Srgba, convert::FromColorUnclamped};
|
||||
|
||||
/// Get an array of 100 colors with a specific hue and chroma
|
||||
/// over the full range of lightness.
|
||||
|
|
@ -81,7 +80,6 @@ pub fn get_text(
|
|||
step_array: &[Srgba],
|
||||
fallback: &Srgba,
|
||||
tint_array: Option<&[Srgba]>,
|
||||
alpha: f32,
|
||||
) -> Srgba {
|
||||
assert!(step_array.len() == 100);
|
||||
let step_array = if let Some(tint_array) = tint_array {
|
||||
|
|
@ -91,28 +89,11 @@ pub fn get_text(
|
|||
step_array
|
||||
};
|
||||
|
||||
let alpha_extra_steps = if alpha < 1.0 {
|
||||
((1. - alpha) * 100.0).round() as usize
|
||||
} else {
|
||||
0
|
||||
};
|
||||
let is_dark = base_index < 60;
|
||||
|
||||
let index = get_index(
|
||||
base_index,
|
||||
70 + alpha_extra_steps,
|
||||
step_array.len(),
|
||||
is_dark,
|
||||
)
|
||||
.or_else(|| {
|
||||
get_index(
|
||||
base_index,
|
||||
50 + alpha_extra_steps,
|
||||
step_array.len(),
|
||||
is_dark,
|
||||
)
|
||||
})
|
||||
.unwrap_or(if is_dark { 99 } else { 0 });
|
||||
let index = get_index(base_index, 70, step_array.len(), is_dark)
|
||||
.or_else(|| get_index(base_index, 50, step_array.len(), is_dark))
|
||||
.unwrap_or(if is_dark { 99 } else { 0 });
|
||||
|
||||
*step_array.get(index).unwrap_or(fallback)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
open = "5.3.3"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = [
|
||||
"debug",
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ use cosmic::app::{Core, Settings, Task};
|
|||
use cosmic::executor;
|
||||
use cosmic::iced::{alignment, Length, Size};
|
||||
use cosmic::prelude::*;
|
||||
use cosmic::widget::about::About;
|
||||
use cosmic::widget::{self, nav_bar};
|
||||
use cosmic::widget::{self, about::About, nav_bar};
|
||||
|
||||
/// Runs application with these settings
|
||||
#[rustfmt::skip]
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ tracing = "0.1"
|
|||
env_logger = "0.10.2"
|
||||
log = "0.4.29"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
default-features = false
|
||||
features = ["applet-token"]
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ use cosmic::iced::core::window;
|
|||
use cosmic::iced::window::Id;
|
||||
use cosmic::iced::{Length, Rectangle};
|
||||
use cosmic::surface::action::{app_popup, destroy_popup};
|
||||
use cosmic::widget::dropdown::popup_dropdown;
|
||||
use cosmic::widget::{list_column, settings, toggler};
|
||||
use cosmic::widget::{dropdown::popup_dropdown, list_column, settings, toggler};
|
||||
use cosmic::Element;
|
||||
|
||||
const ID: &str = "com.system76.CosmicAppletExample";
|
||||
|
|
@ -101,7 +100,6 @@ impl cosmic::Application for Window {
|
|||
Message::Surface(destroy_popup(id))
|
||||
} else {
|
||||
Message::Surface(app_popup::<Window>(
|
||||
|_| Default::default(),
|
||||
move |state: &mut Window| {
|
||||
let new_id = Id::unique();
|
||||
state.popup = Some(new_id);
|
||||
|
|
@ -124,6 +122,8 @@ impl cosmic::Application for Window {
|
|||
},
|
||||
Some(Box::new(move |state: &Window| {
|
||||
let content_list = list_column()
|
||||
.padding(5)
|
||||
.spacing(0)
|
||||
.add(settings::item(
|
||||
"Example row",
|
||||
cosmic::widget::container(
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ edition = "2021"
|
|||
|
||||
[features]
|
||||
default = ["wayland"]
|
||||
wayland = ["libcosmic-yoda/wayland"]
|
||||
wayland = ["libcosmic/wayland"]
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.11"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = [
|
||||
"debug",
|
||||
|
|
@ -22,5 +22,4 @@ features = [
|
|||
"surface-message",
|
||||
"multi-window",
|
||||
"wgpu",
|
||||
"wayland",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@
|
|||
|
||||
use cosmic::app::Settings;
|
||||
use cosmic::iced::{Alignment, Length, Size};
|
||||
use cosmic::prelude::*;
|
||||
use cosmic::widget::menu::{self, KeyBind};
|
||||
use cosmic::widget::{nav_bar, RcElementWrapper};
|
||||
use cosmic::{executor, iced, widget, Core};
|
||||
use cosmic::widget::nav_bar;
|
||||
use cosmic::{executor, iced, prelude::*, widget, Core};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
||||
|
|
@ -43,7 +42,7 @@ pub enum Action {
|
|||
impl widget::menu::Action for Action {
|
||||
type Message = Message;
|
||||
|
||||
fn message(&self) -> Self::Message {
|
||||
fn message(&self) -> Message {
|
||||
match self {
|
||||
Action::Hi => Message::Hi,
|
||||
Action::Hi2 => Message::Hi2,
|
||||
|
|
@ -52,25 +51,6 @@ impl widget::menu::Action for Action {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Action2 {
|
||||
Hi,
|
||||
Hi2,
|
||||
Hi3,
|
||||
}
|
||||
|
||||
impl widget::menu::Action for Action2 {
|
||||
type Message = cosmic::Action<Message>;
|
||||
|
||||
fn message(&self) -> Self::Message {
|
||||
cosmic::Action::App(match self {
|
||||
Action2::Hi => Message::Hi,
|
||||
Action2::Hi2 => Message::Hi2,
|
||||
Action2::Hi3 => Message::Hi3,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs application with these settings
|
||||
#[rustfmt::skip]
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
@ -103,7 +83,6 @@ pub enum Message {
|
|||
Hi2,
|
||||
Hi3,
|
||||
Tick,
|
||||
ValueChanged(f32),
|
||||
}
|
||||
|
||||
/// The [`App`] stores application-specific state.
|
||||
|
|
@ -115,7 +94,6 @@ pub struct App {
|
|||
hidden: bool,
|
||||
keybinds: HashMap<KeyBind, Action>,
|
||||
progress: f32,
|
||||
progress_slider: f32,
|
||||
}
|
||||
|
||||
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
||||
|
|
@ -158,7 +136,6 @@ impl cosmic::Application for App {
|
|||
hidden: true,
|
||||
keybinds: HashMap::new(),
|
||||
progress: 0.0,
|
||||
progress_slider: 0.0,
|
||||
};
|
||||
|
||||
let command = app.update_title();
|
||||
|
|
@ -177,70 +154,6 @@ impl cosmic::Application for App {
|
|||
self.update_title()
|
||||
}
|
||||
|
||||
fn nav_context_menu(&self) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
||||
Some(menu::nav_context(
|
||||
&HashMap::new(),
|
||||
vec![
|
||||
vec![cosmic::widget::menu::Item::Button(
|
||||
"Hi",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
)],
|
||||
vec![
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 2",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 22",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
],
|
||||
vec![
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 3",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 33",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 333",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
],
|
||||
vec![
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 44",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 4444",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 444444",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
cosmic::widget::menu::Item::Button(
|
||||
"Hi 444444444444",
|
||||
Some(cosmic::widget::icon::from_name("screenshot-window-symbolic").into()),
|
||||
Action2::Hi,
|
||||
),
|
||||
],
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
/// Handle application events here.
|
||||
fn update(&mut self, message: Self::Message) -> cosmic::app::Task<Self::Message> {
|
||||
match message {
|
||||
|
|
@ -271,9 +184,6 @@ impl cosmic::Application for App {
|
|||
Message::Tick => {
|
||||
self.progress = (self.progress + 0.01) % 1.0;
|
||||
}
|
||||
Message::ValueChanged(value) => {
|
||||
self.progress_slider = value;
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -290,7 +200,7 @@ impl cosmic::Application for App {
|
|||
.map_or("No page selected", String::as_str);
|
||||
|
||||
let centered = widget::container(
|
||||
widget::column::with_capacity(16)
|
||||
widget::column::with_capacity(14)
|
||||
.push(widget::text::body(page_content))
|
||||
.push(
|
||||
widget::text_input::text_input("", &self.input_1)
|
||||
|
|
@ -331,18 +241,6 @@ impl cosmic::Application for App {
|
|||
.progress(self.progress)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(
|
||||
widget::slider(0.0..=1.0, self.progress_slider, Message::ValueChanged)
|
||||
.step(0.001),
|
||||
)
|
||||
.push(
|
||||
widget::progress_bar::linear::Linear::new()
|
||||
.girth(10.0)
|
||||
.progress(self.progress_slider)
|
||||
.width(Length::Fill)
|
||||
.markers([0.25, 0.5, 0.75])
|
||||
.segment_spacing(2),
|
||||
)
|
||||
.push(
|
||||
widget::progress_bar::circular::Circular::new()
|
||||
.size(50.0)
|
||||
|
|
@ -365,7 +263,6 @@ impl cosmic::Application for App {
|
|||
.progress(1.0)
|
||||
.width(Length::Fill),
|
||||
)
|
||||
.push(widget::button::suggested("asdf").on_press(Message::Ignore))
|
||||
.spacing(cosmic::theme::spacing().space_s)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Shrink)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ edition = "2024"
|
|||
[dependencies]
|
||||
jiff = "0.2"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ tracing = "0.1.44"
|
|||
tracing-subscriber = "0.3.22"
|
||||
tracing-log = "0.2.0"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = [
|
||||
"debug",
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ pub enum Message {
|
|||
WindowClose,
|
||||
Surface(cosmic::surface::Action),
|
||||
ToggleHideContent,
|
||||
ToggleSomeAction,
|
||||
WindowNew,
|
||||
}
|
||||
|
||||
|
|
@ -83,7 +82,6 @@ impl cosmic::Application for App {
|
|||
|
||||
/// Handle application events here.
|
||||
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
|
||||
tracing::error!("Clicked {message:?}");
|
||||
match message {
|
||||
Message::Clicked => {
|
||||
self.button_label = format!("Clicked {message:?}");
|
||||
|
|
@ -93,10 +91,9 @@ impl cosmic::Application for App {
|
|||
cosmic::app::Action::Surface(action),
|
||||
));
|
||||
}
|
||||
Message::WindowClose
|
||||
| Message::ToggleHideContent
|
||||
| Message::ToggleSomeAction
|
||||
| Message::WindowNew => {}
|
||||
Message::WindowClose => {}
|
||||
Message::ToggleHideContent => {}
|
||||
Message::WindowNew => {}
|
||||
}
|
||||
|
||||
Task::none()
|
||||
|
|
@ -105,7 +102,7 @@ impl cosmic::Application for App {
|
|||
/// Creates a view after each update.
|
||||
fn view(&self) -> Element<'_, Self::Message> {
|
||||
let widget = cosmic::widget::context_menu(
|
||||
cosmic::widget::button::text(self.button_label.clone()).on_press(Message::Clicked),
|
||||
cosmic::widget::button::text(self.button_label.to_string()).on_press(Message::Clicked),
|
||||
self.context_menu(),
|
||||
)
|
||||
.on_surface_action(Message::Surface);
|
||||
|
|
@ -129,20 +126,12 @@ impl App {
|
|||
menu::Item::Divider,
|
||||
menu::Item::Folder(
|
||||
"View",
|
||||
vec![
|
||||
menu::Item::CheckBox(
|
||||
"Hide content",
|
||||
None,
|
||||
self.hide_content,
|
||||
ContextMenuAction::ToggleHideContent,
|
||||
),
|
||||
menu::Item::CheckBox(
|
||||
"Test content",
|
||||
None,
|
||||
self.hide_content,
|
||||
ContextMenuAction::ToggleSomeAction,
|
||||
),
|
||||
],
|
||||
vec![menu::Item::CheckBox(
|
||||
"Hide content",
|
||||
None,
|
||||
self.hide_content,
|
||||
ContextMenuAction::ToggleHideContent,
|
||||
)],
|
||||
),
|
||||
menu::Item::Divider,
|
||||
menu::Item::Button("Quit", None, ContextMenuAction::WindowClose),
|
||||
|
|
@ -155,7 +144,6 @@ impl App {
|
|||
pub enum ContextMenuAction {
|
||||
WindowClose,
|
||||
ToggleHideContent,
|
||||
ToggleSomeAction,
|
||||
WindowNew,
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +153,6 @@ impl menu::Action for ContextMenuAction {
|
|||
match self {
|
||||
ContextMenuAction::WindowClose => Message::WindowClose,
|
||||
ContextMenuAction::ToggleHideContent => Message::ToggleHideContent,
|
||||
ContextMenuAction::ToggleSomeAction => Message::ToggleSomeAction,
|
||||
ContextMenuAction::WindowNew => Message::WindowNew,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ publish = false
|
|||
[dependencies]
|
||||
apply = "0.3.0"
|
||||
fraction = "0.15.3"
|
||||
libcosmic-yoda = { path = "../..", features = [
|
||||
libcosmic = { path = "../..", features = [
|
||||
"debug",
|
||||
"winit",
|
||||
"tokio",
|
||||
|
|
|
|||
|
|
@ -23,11 +23,15 @@ use cosmic::{
|
|||
Element,
|
||||
};
|
||||
use cosmic_time::{Instant, Timeline};
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::sync::atomic::{AtomicU32, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::vec;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc,
|
||||
},
|
||||
vec,
|
||||
};
|
||||
|
||||
// XXX The use of button is removed because it assigns the same ID to multiple buttons, causing a crash when a11y is enabled...
|
||||
// static BTN: Lazy<id::Id> = Lazy::new(|| id::Id::new("BTN"));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use super::{Page, Window};
|
||||
use cosmic::iced::widget::{column, text};
|
||||
use cosmic::widget::{list_column, settings, toggler};
|
||||
use cosmic::Element;
|
||||
use cosmic::{
|
||||
iced::widget::{column, text},
|
||||
widget::{list_column, settings, toggler},
|
||||
Element,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum Message {
|
||||
|
|
|
|||
|
|
@ -1,17 +1,19 @@
|
|||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use apply::Apply;
|
||||
use cosmic::iced::widget::{checkbox, column, progress_bar, radio, slider, text};
|
||||
use cosmic::iced::{Alignment, Length};
|
||||
use cosmic::iced_core::id;
|
||||
use cosmic::theme::ThemeType;
|
||||
use cosmic::widget::color_picker::ColorPickerUpdate;
|
||||
use cosmic::widget::{
|
||||
button, dropdown, icon, layer_container as container, segmented_button, segmented_control,
|
||||
settings, spin_button, tab_bar, toggler, ColorPickerModel,
|
||||
use cosmic::{
|
||||
cosmic_theme,
|
||||
iced::widget::{checkbox, column, progress_bar, radio, slider, text},
|
||||
iced::{Alignment, Length},
|
||||
iced_core::id,
|
||||
theme::ThemeType,
|
||||
widget::{
|
||||
button, color_picker::ColorPickerUpdate, dropdown, icon, layer_container as container,
|
||||
segmented_button, segmented_control, settings, spin_button, tab_bar, toggler,
|
||||
ColorPickerModel,
|
||||
},
|
||||
Element,
|
||||
};
|
||||
use cosmic::{cosmic_theme, Element};
|
||||
use cosmic_time::{anim, chain, Timeline};
|
||||
use fraction::{Decimal, ToPrimitive};
|
||||
use once_cell::sync::Lazy;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,10 @@
|
|||
use cosmic::iced::widget::{column, container, horizontal_space, image, row, svg, text};
|
||||
use cosmic::iced::Length;
|
||||
use cosmic::widget::{list_column, settings, toggler};
|
||||
use cosmic::{theme, Element};
|
||||
use cosmic::{
|
||||
iced::widget::{column, container, horizontal_space, image, row, svg, text},
|
||||
iced::Length,
|
||||
theme,
|
||||
widget::{list_column, settings, toggler},
|
||||
Element,
|
||||
};
|
||||
|
||||
use super::{Page, SubPage, Window};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
use cosmic::iced::widget::{horizontal_space, row, text};
|
||||
use cosmic::iced::Length;
|
||||
use cosmic::widget::{icon, list_column, settings};
|
||||
use cosmic::Element;
|
||||
use cosmic::{
|
||||
iced::widget::{horizontal_space, row, text},
|
||||
iced::Length,
|
||||
widget::{icon, list_column, settings},
|
||||
Element,
|
||||
};
|
||||
|
||||
use super::{Message, Page, SubPage, Window};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ edition = "2021"
|
|||
tracing = "0.1.44"
|
||||
tracing-subscriber = "0.3.22"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "wgpu", "tokio"]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ tracing = "0.1.44"
|
|||
tracing-subscriber = "0.3.22"
|
||||
tracing-log = "0.2.0"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ use std::{env, process};
|
|||
use cosmic::app::{Core, Settings, Task};
|
||||
use cosmic::iced::alignment::{Horizontal, Vertical};
|
||||
use cosmic::iced::keyboard::Key;
|
||||
use cosmic::iced::{event, keyboard, window, Event, Length, Size, Subscription};
|
||||
use cosmic::iced::window;
|
||||
use cosmic::iced::{Length, Size};
|
||||
use cosmic::widget::menu::action::MenuAction;
|
||||
use cosmic::widget::menu::key_bind::{KeyBind, Modifier};
|
||||
use cosmic::widget::menu::key_bind::KeyBind;
|
||||
use cosmic::widget::menu::key_bind::Modifier;
|
||||
use cosmic::widget::menu::{self, ItemHeight, ItemWidth};
|
||||
use cosmic::widget::RcElementWrapper;
|
||||
use cosmic::{executor, Element};
|
||||
|
|
@ -112,26 +114,6 @@ impl cosmic::Application for App {
|
|||
vec![menu_bar(&self.config, &self.key_binds)]
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Self::Message> {
|
||||
event::listen_with(|event, _status, _window_id| {
|
||||
let Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
key,
|
||||
modifiers,
|
||||
physical_key,
|
||||
..
|
||||
}) = event
|
||||
else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let key_binds = key_binds();
|
||||
key_binds
|
||||
.iter()
|
||||
.find(|(key_bind, _)| key_bind.matches(modifiers, &key, Some(&physical_key)))
|
||||
.map(|(_, action)| action.message())
|
||||
})
|
||||
}
|
||||
|
||||
/// Handle application events here.
|
||||
fn update(&mut self, message: Self::Message) -> Task<Self::Message> {
|
||||
match message {
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libcosmic-yoda = { path = "../..", features = ["debug", "winit", "tokio", "single-instance", "wgpu", "wayland"] }
|
||||
libcosmic = { path = "../..", features = ["debug", "winit", "tokio", "single-instance", "wgpu", "wayland"] }
|
||||
|
|
|
|||
|
|
@ -1,11 +1,13 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use cosmic::app::Core;
|
||||
use cosmic::iced::core::{id, Alignment, Length, Point};
|
||||
use cosmic::iced::widget::{column, container, scrollable, text};
|
||||
use cosmic::iced::{self, event, window, Subscription};
|
||||
use cosmic::prelude::*;
|
||||
use cosmic::widget::{button, header_bar, selectable_text};
|
||||
use cosmic::{
|
||||
app::Core,
|
||||
iced::core::{id, Alignment, Length, Point},
|
||||
iced::widget::{column, container, scrollable, text},
|
||||
iced::{self, event, window, Subscription},
|
||||
prelude::*,
|
||||
widget::{button, header_bar},
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Message {
|
||||
|
|
@ -132,14 +134,10 @@ impl cosmic::Application for MultiWindow {
|
|||
let new_window_button = button::custom(text("New Window")).on_press(Message::NewWindow);
|
||||
|
||||
let content = scrollable(
|
||||
column![
|
||||
input,
|
||||
new_window_button,
|
||||
selectable_text::title1("I'm selectable...")
|
||||
]
|
||||
.spacing(50)
|
||||
.width(Length::Fill)
|
||||
.align_x(Alignment::Center),
|
||||
column![input, new_window_button]
|
||||
.spacing(50)
|
||||
.width(Length::Fill)
|
||||
.align_x(Alignment::Center),
|
||||
);
|
||||
|
||||
let window_content = container(container(content).center_x(Length::Fixed(200.)))
|
||||
|
|
@ -150,14 +148,7 @@ impl cosmic::Application for MultiWindow {
|
|||
if id == self.core.main_window_id().unwrap() {
|
||||
window_content.into()
|
||||
} else {
|
||||
container(column![
|
||||
header_bar()
|
||||
.on_close(Message::CloseWindow(id))
|
||||
.focused(focused),
|
||||
window_content
|
||||
])
|
||||
.class(cosmic::theme::Container::WindowBackground)
|
||||
.into()
|
||||
column![header_bar().focused(focused), window_content].into()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ tracing = "0.1.44"
|
|||
tracing-subscriber = "0.3.22"
|
||||
tracing-log = "0.2.0"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use std::collections::HashMap;
|
|||
|
||||
use cosmic::app::{Core, Settings, Task};
|
||||
use cosmic::iced::Size;
|
||||
use cosmic::widget::{menu, nav_bar, space};
|
||||
use cosmic::widget::{menu, nav_bar};
|
||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -127,20 +127,18 @@ impl cosmic::Application for App {
|
|||
Some(&self.nav_model)
|
||||
}
|
||||
|
||||
/// The context menu list.
|
||||
fn nav_context_menu(&self) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
||||
Some(menu::nav_context(
|
||||
/// The context menu to display for the given nav bar item ID.
|
||||
fn nav_context_menu(
|
||||
&self,
|
||||
id: nav_bar::Id,
|
||||
) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
||||
Some(menu::items(
|
||||
&HashMap::new(),
|
||||
self.nav_model
|
||||
.iter()
|
||||
.map(|e| {
|
||||
vec![
|
||||
menu::Item::Button("Move Up", None, NavMenuAction::MoveUp(e)),
|
||||
menu::Item::Button("Move Down", None, NavMenuAction::MoveDown(e)),
|
||||
menu::Item::Button("Delete", None, NavMenuAction::Delete(e)),
|
||||
]
|
||||
})
|
||||
.collect(),
|
||||
vec![
|
||||
menu::Item::Button("Move Up", None, NavMenuAction::MoveUp(id)),
|
||||
menu::Item::Button("Move Down", None, NavMenuAction::MoveDown(id)),
|
||||
menu::Item::Button("Delete", None, NavMenuAction::Delete(id)),
|
||||
],
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ edition = "2021"
|
|||
|
||||
[features]
|
||||
default = ["xdg-portal"]
|
||||
rfd = ["libcosmic-yoda/rfd"]
|
||||
xdg-portal = ["libcosmic-yoda/xdg-portal"]
|
||||
rfd = ["libcosmic/rfd"]
|
||||
xdg-portal = ["libcosmic/xdg-portal"]
|
||||
|
||||
[dependencies]
|
||||
apply = "0.3.0"
|
||||
|
|
@ -15,6 +15,6 @@ tracing = "0.1.44"
|
|||
tracing-subscriber = "0.3.22"
|
||||
url = "2.5.8"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
features = ["debug", "winit", "wgpu", "wayland", "tokio"]
|
||||
path = "../../"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
fraction = "0.15.3"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
||||
path = "../.."
|
||||
default-features = false
|
||||
|
|
|
|||
|
|
@ -1,8 +1,15 @@
|
|||
use cosmic::app::{Core, Task};
|
||||
use cosmic::iced::alignment::{Horizontal, Vertical};
|
||||
use cosmic::iced::{self, Alignment, Length, Size};
|
||||
use cosmic::iced::Length;
|
||||
use cosmic::widget::{column, container, spin_button};
|
||||
use cosmic::{Application, Apply, Element};
|
||||
use cosmic::Apply;
|
||||
use cosmic::{
|
||||
app::{Core, Task},
|
||||
iced::{
|
||||
self,
|
||||
alignment::{Horizontal, Vertical},
|
||||
Alignment, Size,
|
||||
},
|
||||
Application, Element,
|
||||
};
|
||||
use fraction::Decimal;
|
||||
|
||||
pub struct SpinButtonExamplApp {
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "wgpu", "tokio", "xdg-portal"]
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@
|
|||
|
||||
use cosmic::app::{Core, Settings, Task};
|
||||
use cosmic::iced::Subscription;
|
||||
use cosmic::prelude::*;
|
||||
use cosmic::{executor, widget};
|
||||
use cosmic::{executor, prelude::*, widget};
|
||||
|
||||
/// Runs application with these settings
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ tracing-subscriber = "0.3.22"
|
|||
tracing-log = "0.2.0"
|
||||
chrono = "*"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
||||
path = "../.."
|
||||
|
|
|
|||
|
|
@ -9,7 +9,8 @@ use chrono::Datelike;
|
|||
use cosmic::app::{Core, Settings, Task};
|
||||
use cosmic::iced::Size;
|
||||
use cosmic::prelude::*;
|
||||
use cosmic::widget::{self, nav_bar, table};
|
||||
use cosmic::widget::table;
|
||||
use cosmic::widget::{self, nav_bar};
|
||||
use cosmic::{executor, iced};
|
||||
|
||||
#[derive(Debug, Default, PartialEq, Eq, Clone, Copy, Hash)]
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ tracing = "0.1.44"
|
|||
tracing-subscriber = "0.3.22"
|
||||
tracing-log = "0.2.0"
|
||||
|
||||
[dependencies.libcosmic-yoda]
|
||||
[dependencies.libcosmic]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "wgpu", "tokio", "xdg-portal"]
|
||||
|
|
|
|||
|
|
@ -1,3 +0,0 @@
|
|||
license = Llicència
|
||||
close = Tancar
|
||||
links = Enllaços
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
close = Κλείσιμο
|
||||
february = Φεβρουάριος { $year }
|
||||
documenters = Τεκμηριωτές
|
||||
november = Νοέμβριος { $year }
|
||||
may = Μάιος { $year }
|
||||
april = Απρίλιος { $year }
|
||||
translators = Μεταφραστές
|
||||
artists = Καλλιτέχνες
|
||||
license = Άδεια χρήσης
|
||||
december = Δεκέμβριος { $year }
|
||||
links = Σύνδεσμοι
|
||||
march = Μάρτιος { $year }
|
||||
june = Ιούνιος { $year }
|
||||
august = Αύγουστος { $year }
|
||||
developers = Προγραμματιστές
|
||||
july = Ιούλιος { $year }
|
||||
september = Σεπτέμβριος { $year }
|
||||
designers = Σχεδιαστές
|
||||
october = Οκτώβριος { $year }
|
||||
january = Ιανουάριος { $year }
|
||||
monday = Δευτέρα
|
||||
mon = Δευ
|
||||
tuesday = Τρίτη
|
||||
tue = Τρί
|
||||
wednesday = Τετάρτη
|
||||
wed = Τετ
|
||||
thursday = Πέμπτη
|
||||
thu = Πέμ
|
||||
friday = Παρασκευή
|
||||
fri = Παρ
|
||||
saturday = Σάββατο
|
||||
sat = Σάβ
|
||||
sunday = Κυριακή
|
||||
sun = Κυρ
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
close = Pechar
|
||||
license = Licenza
|
||||
links = Enderezos
|
||||
developers = Desenvolvedores
|
||||
designers = Deseñadores
|
||||
artists = Artistas
|
||||
translators = Traductores
|
||||
documenters = Documentadores
|
||||
january = Xaneiro { $year }
|
||||
february = Febreiro { $year }
|
||||
march = Marzo { $year }
|
||||
april = Abril { $year }
|
||||
may = Maio { $year }
|
||||
june = Xuño { $year }
|
||||
july = Xullo { $year }
|
||||
august = Agosto { $year }
|
||||
september = Setembro { $year }
|
||||
october = Outubro { $year }
|
||||
november = Novembro { $year }
|
||||
december = Decembro { $year }
|
||||
monday = Luns
|
||||
mon = Lun
|
||||
tuesday = Martes
|
||||
tue = Mar
|
||||
wednesday = Mércores
|
||||
wed = Mér
|
||||
thursday = Xoves
|
||||
thu = Xov
|
||||
friday = Venres
|
||||
fri = Ven
|
||||
saturday = Sábado
|
||||
sat = Sáb
|
||||
sunday = Domingo
|
||||
sun = Dom
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue