Compare commits
81 commits
3daac73eff
...
2fe2d35286
| Author | SHA1 | Date | |
|---|---|---|---|
| 2fe2d35286 | |||
| 9e9ae942c4 | |||
| 18c2326f34 | |||
| 37829fd899 | |||
| ca66c7c1b0 | |||
| ce5e3b6eca | |||
| 2c91d818c9 | |||
| 9512f77a58 | |||
| 4e1080108c | |||
| c85efc6180 | |||
| 7c0b3ff1b7 | |||
| 695c602ba1 | |||
| 45eaaf1605 | |||
| 261a211647 | |||
| b7c81907ba | |||
| 6219084eb6 | |||
|
|
20dfd7c236 | ||
|
|
be499d01af | ||
|
|
bb0145c566 | ||
|
|
a365d45091 | ||
|
|
cad4be9401 | ||
|
|
822d3b2a0f | ||
|
|
46b25d4218 | ||
|
|
9855d8b6fd | ||
|
|
5ea8a86959 | ||
|
|
e3a4e739e3 | ||
|
|
46c717fd57 | ||
|
|
94d2c18a23 | ||
|
|
cde1989e78 | ||
|
|
f5fc392ba4 | ||
|
|
f04437cee5 | ||
|
|
e939d334f6 | ||
|
|
217b4abe99 | ||
|
|
0c336d77e4 | ||
|
|
00067dc5b8 | ||
|
|
19f4a289e5 | ||
|
|
43de9e3e77 | ||
|
|
033dfe6c57 | ||
|
|
76954a6524 | ||
|
|
4a7ed015f6 | ||
|
|
991a8f2c78 | ||
|
|
8d2dbb3f23 | ||
|
|
ac4a1ab259 | ||
|
|
f268bb86cf | ||
|
|
58980231b9 | ||
|
|
bd56f82ca0 | ||
|
|
90ac265ee0 | ||
|
|
40792f21de | ||
|
|
7d62b193f4 | ||
|
|
408d37169a | ||
|
|
5fca59df70 | ||
|
|
5938776c26 | ||
|
|
0ee5131670 | ||
|
|
fb4628e460 | ||
|
|
1fafd57578 | ||
|
|
bf5ffcc2af | ||
|
|
2d7f66d528 | ||
|
|
732785a50c | ||
|
|
cadb773377 | ||
|
|
9e77210740 | ||
|
|
cfd5220c1e | ||
|
|
19142fd38e | ||
|
|
d0b164d216 | ||
|
|
439109db04 | ||
|
|
9395a23a0b | ||
|
|
7ac8f74825 | ||
|
|
72ddeaa5fb | ||
|
|
7ecc6a32d6 | ||
|
|
aa337c2a16 | ||
|
|
18d76659f2 | ||
|
|
18a7a75e33 | ||
|
|
e7f37f3e81 | ||
|
|
13fb598517 | ||
|
|
417923fbaf | ||
|
|
76bc13ff40 | ||
|
|
6e116097c0 | ||
|
|
e7f278d17f | ||
|
|
8df1858e5d | ||
|
|
f79ccb8cba | ||
|
|
a991d7bb43 | ||
|
|
4d36a2ee5f |
192 changed files with 4333 additions and 1351 deletions
78
.github/workflows/ci.yml
vendored
78
.github/workflows/ci.yml
vendored
|
|
@ -6,26 +6,20 @@ on:
|
|||
- master
|
||||
pull_request:
|
||||
|
||||
|
||||
jobs:
|
||||
format:
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
- name: Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
components: rustfmt
|
||||
- name: Cargo cache
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }}
|
||||
rustflags: ""
|
||||
- name: Format
|
||||
run: cargo fmt -- --check
|
||||
run: cargo +nightly fmt -- --check
|
||||
|
||||
tests:
|
||||
needs:
|
||||
|
|
@ -34,38 +28,28 @@ 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-22.04
|
||||
- --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
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
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: dtolnay/rust-toolchain@stable
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
rustflags: ""
|
||||
- name: Test features
|
||||
run: cargo test ${{ matrix.test_args }} -- --test-threads=1
|
||||
env:
|
||||
|
|
@ -82,28 +66,18 @@ jobs:
|
|||
- "open-dialog"
|
||||
- "context-menu"
|
||||
- "nav-context"
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
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: dtolnay/rust-toolchain@stable
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
rustflags: ""
|
||||
- name: Check example
|
||||
run: cargo check -p "${{ matrix.examples }}"
|
||||
env:
|
||||
|
|
|
|||
7
.github/workflows/pages.yml
vendored
7
.github/workflows/pages.yml
vendored
|
|
@ -11,13 +11,14 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
submodules: recursive
|
||||
- name: Install Rust nightly
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
toolchain: nightly-2026-04-27
|
||||
rustflags: ""
|
||||
- name: System dependencies
|
||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||
- name: Build documentation
|
||||
|
|
@ -29,7 +30,7 @@ jobs:
|
|||
-p libcosmic \
|
||||
--verbose --features tokio,winit,wayland,desktop,single-instance,applet,xdg-portal,multi-window
|
||||
- name: Deploy documentation
|
||||
uses: peaceiris/actions-gh-pages@v3
|
||||
uses: peaceiris/actions-gh-pages@v4
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./target/doc
|
||||
|
|
|
|||
100
Cargo.toml
100
Cargo.toml
|
|
@ -1,6 +1,11 @@
|
|||
[package]
|
||||
name = "libcosmic"
|
||||
version = "1.0.0"
|
||||
# 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"
|
||||
edition = "2024"
|
||||
rust-version = "1.93"
|
||||
|
||||
|
|
@ -9,12 +14,10 @@ name = "cosmic"
|
|||
|
||||
[features]
|
||||
default = [
|
||||
"winit",
|
||||
"tokio",
|
||||
"a11y",
|
||||
"dbus-config",
|
||||
"x11",
|
||||
"iced-wayland",
|
||||
"wayland",
|
||||
"multi-window",
|
||||
]
|
||||
advanced-shaping = ["iced/advanced-shaping"]
|
||||
|
|
@ -35,7 +38,6 @@ animated-image = [
|
|||
autosize = []
|
||||
applet = [
|
||||
"autosize",
|
||||
"winit",
|
||||
"wayland",
|
||||
"tokio",
|
||||
"cosmic-panel-config",
|
||||
|
|
@ -69,7 +71,7 @@ desktop-systemd-scope = ["desktop", "dep:zbus"]
|
|||
# Enables keycode serialization
|
||||
serde-keycode = ["iced_core/serde"]
|
||||
# Prevents multiple separate process instances.
|
||||
single-instance = ["iced_winit/single-instance", "zbus/blocking-api", "ron"]
|
||||
single-instance = ["zbus/blocking-api", "ron"]
|
||||
# smol async runtime
|
||||
smol = ["dep:smol", "iced/smol", "zbus?/async-io", "rfd?/async-std"]
|
||||
tokio = [
|
||||
|
|
@ -81,32 +83,34 @@ tokio = [
|
|||
"cosmic-config/tokio",
|
||||
]
|
||||
# Tokio async runtime
|
||||
# Wayland window support
|
||||
iced-wayland = [
|
||||
# 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.
|
||||
wayland = [
|
||||
"ashpd?/wayland",
|
||||
"autosize",
|
||||
"iced/winit",
|
||||
"iced/wayland",
|
||||
"iced_winit/wayland",
|
||||
"surface-message",
|
||||
]
|
||||
wayland = [
|
||||
"iced-wayland",
|
||||
"iced_runtime/cctk",
|
||||
"iced_winit/cctk",
|
||||
"iced_wgpu/cctk",
|
||||
"iced/cctk",
|
||||
"dep:iced_winit",
|
||||
"dep:cctk",
|
||||
"surface-message",
|
||||
]
|
||||
surface-message = []
|
||||
# multi-window support
|
||||
multi-window = []
|
||||
# Render with wgpu
|
||||
wgpu = ["iced/wgpu", "iced_wgpu"]
|
||||
# X11 window support via winit
|
||||
winit = ["iced/winit", "iced_winit"]
|
||||
winit_debug = ["winit", "debug"]
|
||||
winit_tokio = ["winit", "tokio"]
|
||||
winit_wgpu = ["winit", "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 = []
|
||||
# Enables XDG portal integrations
|
||||
xdg-portal = ["ashpd"]
|
||||
qr_code = ["iced/qr_code"]
|
||||
|
|
@ -119,7 +123,6 @@ async-std = [
|
|||
"zbus?/async-io",
|
||||
"iced/async-std",
|
||||
]
|
||||
x11 = ["iced/x11", "iced_winit/x11"]
|
||||
|
||||
[dependencies]
|
||||
apply = "0.3.0"
|
||||
|
|
@ -127,10 +130,10 @@ ashpd = { version = "0.12.3", default-features = false, optional = true }
|
|||
async-fs = { version = "2.2", optional = true }
|
||||
async-std = { workspace = true, optional = true }
|
||||
auto_enums = "0.8.8"
|
||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "c253ec1", optional = true }
|
||||
cctk = { path = "../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit", optional = true }
|
||||
jiff = "0.2"
|
||||
cosmic-config = { path = "cosmic-config" }
|
||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
|
||||
cosmic-settings-config = { path = "../cosmic-settings-daemon/config", optional = true }
|
||||
# Internationalization
|
||||
i18n-embed = { version = "0.16.0", features = [
|
||||
"fluent-system",
|
||||
|
|
@ -170,15 +173,16 @@ url = "2.5.8"
|
|||
zbus = { workspace = true, optional = true }
|
||||
float-cmp = "0.10.0"
|
||||
ron = { workspace = true, optional = true }
|
||||
enumflags2 = "0.7.12"
|
||||
|
||||
# Enable DBus feature on Linux targets
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
cosmic-config = { path = "cosmic-config", features = ["dbus"] }
|
||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
||||
zbus.workspace = true
|
||||
cosmic-settings-daemon = { path = "../dbus-settings-bindings/cosmic-settings-daemon" }
|
||||
zbus = { version = "5.14.0", default-features = false }
|
||||
|
||||
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
||||
freedesktop-icons = { package = "cosmic-freedesktop-icons", git = "https://github.com/pop-os/freedesktop-icons" }
|
||||
freedesktop-icons = { package = "cosmic-freedesktop-icons", path = "../cosmic-freedesktop-icons" }
|
||||
freedesktop-desktop-entry = { version = "0.8.1", optional = true }
|
||||
shlex = { version = "1.3.0", optional = true }
|
||||
|
||||
|
|
@ -224,28 +228,58 @@ 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]
|
||||
git = "https://github.com/pop-os/cosmic-panel"
|
||||
# path = "../cosmic-panel/cosmic-panel-config"
|
||||
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" }
|
||||
|
||||
[workspace]
|
||||
members = [
|
||||
"cosmic-config",
|
||||
"cosmic-config-derive",
|
||||
"cosmic-theme",
|
||||
"examples/*",
|
||||
]
|
||||
exclude = ["iced"]
|
||||
# examples/* excluded — many depend on the removed winit/x11 features.
|
||||
# They will be revisited and adapted in a later phase.
|
||||
exclude = ["iced", "examples"]
|
||||
|
||||
[workspace.dependencies]
|
||||
async-std = "1.13"
|
||||
|
|
@ -256,7 +290,15 @@ serde = "1.0"
|
|||
thiserror = "2.0"
|
||||
tracing = "0.1"
|
||||
tokio = "1.52"
|
||||
zbus = {version = "5.15", default-features = false}
|
||||
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
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.27.0"
|
||||
|
|
|
|||
|
|
@ -1,17 +1,32 @@
|
|||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{self};
|
||||
use syn;
|
||||
|
||||
#[proc_macro_derive(CosmicConfigEntry, attributes(version, id))]
|
||||
#[proc_macro_derive(CosmicConfigEntry, attributes(version, id, cosmic_config_entry))]
|
||||
pub fn cosmic_config_entry_derive(input: TokenStream) -> TokenStream {
|
||||
// Construct a representation of Rust code as a syntax tree
|
||||
// that we can manipulate
|
||||
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
|
||||
|
|
@ -48,19 +63,54 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||
|
||||
let write_each_config_field = fields.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
quote! {
|
||||
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
|
||||
let with = match get_cosmic_config_attrs(field) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => {
|
||||
return e.to_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(with) = with {
|
||||
quote! {
|
||||
{
|
||||
let conv = self.#field_name.clone().into();
|
||||
cosmic_config::ConfigSet::set::<#with>(&tx, stringify!(#field_name), conv)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let get_each_config_field = fields.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
let field_type = &field.ty;
|
||||
quote! {
|
||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||
Ok(#field_name) => default.#field_name = #field_name,
|
||||
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
|
||||
Err(e) => errors.push(e),
|
||||
let with = match get_cosmic_config_attrs(field) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => {
|
||||
return e.to_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(with) = with {
|
||||
quote! {
|
||||
match cosmic_config::ConfigGet::get::<#with>(config, stringify!(#field_name)) {
|
||||
Ok(value) => {
|
||||
default.#field_name = value.into();
|
||||
}
|
||||
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||
Ok(#field_name) => default.#field_name = #field_name,
|
||||
Err(why) if matches!(why, cosmic_config::Error::NoConfigDirectory) => (),
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -68,17 +118,39 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
|||
let update_each_config_field = fields.iter().map(|field| {
|
||||
let field_name = &field.ident;
|
||||
let field_type = &field.ty;
|
||||
quote! {
|
||||
stringify!(#field_name) => {
|
||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||
Ok(value) => {
|
||||
if self.#field_name != value {
|
||||
keys.push(stringify!(#field_name));
|
||||
}
|
||||
self.#field_name = value;
|
||||
},
|
||||
Err(e) => {
|
||||
errors.push(e);
|
||||
let with = match get_cosmic_config_attrs(field) {
|
||||
Ok(attrs) => attrs,
|
||||
Err(e) => {
|
||||
return e.to_compile_error();
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(with) = with {
|
||||
quote! {
|
||||
stringify!(#field_name) => {
|
||||
match cosmic_config::ConfigGet::get::<#with>(config, stringify!(#field_name)) {
|
||||
Ok(value) => {
|
||||
let value = value.into();
|
||||
if self.#field_name != value {
|
||||
keys.push(stringify!(#field_name));
|
||||
}
|
||||
self.#field_name = value;
|
||||
},
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
stringify!(#field_name) => {
|
||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||
Ok(value) => {
|
||||
if self.#field_name != value {
|
||||
keys.push(stringify!(#field_name));
|
||||
}
|
||||
self.#field_name = value;
|
||||
},
|
||||
Err(e) => errors.push(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ macro = ["cosmic-config-derive"]
|
|||
subscription = ["iced_futures"]
|
||||
|
||||
[dependencies]
|
||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
||||
zbus = { workspace = true, default-features = false, optional = true }
|
||||
cosmic-settings-daemon = { path = "../../dbus-settings-bindings/cosmic-settings-daemon", 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"
|
||||
|
|
|
|||
|
|
@ -175,6 +175,7 @@ pub trait ConfigSet {
|
|||
pub struct Config {
|
||||
system_path: Option<PathBuf>,
|
||||
user_path: Option<PathBuf>,
|
||||
previous: Option<Box<Config>>,
|
||||
}
|
||||
|
||||
/// Check that the name is relative and doesn't contain . or ..
|
||||
|
|
@ -193,9 +194,13 @@ fn sanitize_name(name: &str) -> Result<&Path, Error> {
|
|||
impl Config {
|
||||
/// Get a system config for the given name and config version
|
||||
pub fn system(name: &str, version: u64) -> Result<Self, Error> {
|
||||
Self::system_inner(name, version, true)
|
||||
}
|
||||
|
||||
fn system_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||
#[cfg(unix)]
|
||||
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(path);
|
||||
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(&path);
|
||||
|
||||
#[cfg(windows)]
|
||||
let system_path =
|
||||
|
|
@ -205,6 +210,13 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path,
|
||||
user_path: None,
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::system_inner(name, version - 1, false)
|
||||
.ok()
|
||||
.map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -212,6 +224,10 @@ impl Config {
|
|||
// Use folder at XDG config/name for config storage, return Config if successful
|
||||
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||
pub fn new(name: &str, version: u64) -> Result<Self, Error> {
|
||||
Self::new_inner(name, version, true)
|
||||
}
|
||||
|
||||
fn new_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||
// Look for [name]/v[version]
|
||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||
|
||||
|
|
@ -236,15 +252,29 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path,
|
||||
user_path: Some(user_path),
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::new_inner(name, version - 1, false).ok().map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Get config for the given application name and config version and custom path.
|
||||
pub fn with_custom_path(name: &str, version: u64, custom_path: PathBuf) -> Result<Self, Error> {
|
||||
Self::with_custom_path_inner(name, version, custom_path, true)
|
||||
}
|
||||
|
||||
fn with_custom_path_inner(
|
||||
name: &str,
|
||||
version: u64,
|
||||
custom_path: PathBuf,
|
||||
look_for_previous: bool,
|
||||
) -> Result<Self, Error> {
|
||||
// Look for [name]/v[version]
|
||||
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||
|
||||
let mut user_path = custom_path;
|
||||
let mut user_path = custom_path.clone();
|
||||
user_path.push("cosmic");
|
||||
user_path.push(path);
|
||||
// Create new configuration directory if not found.
|
||||
|
|
@ -254,6 +284,13 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path: None,
|
||||
user_path: Some(user_path),
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::with_custom_path_inner(name, version - 1, custom_path.clone(), false)
|
||||
.ok()
|
||||
.map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -263,6 +300,10 @@ impl Config {
|
|||
// Use folder at XDG config/name for config storage, return Config if successful
|
||||
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||
pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
|
||||
Self::new_state_inner(name, version, true)
|
||||
}
|
||||
|
||||
fn new_state_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||
// Look for [name]/v[version]
|
||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||
|
||||
|
|
@ -276,11 +317,27 @@ 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
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
/// Get data for the given application name and config version.
|
||||
/// pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
|
||||
pub fn new_data(name: &str, version: u64) -> Result<Self, Error> {
|
||||
Self::new_data_inner(name, version, true)
|
||||
}
|
||||
|
||||
pub fn new_data_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));
|
||||
|
||||
|
|
@ -294,6 +351,13 @@ impl Config {
|
|||
Ok(Self {
|
||||
system_path: None,
|
||||
user_path: Some(user_path),
|
||||
previous: if version > 1 && look_for_previous {
|
||||
Self::new_data_inner(name, version - 1, false)
|
||||
.ok()
|
||||
.map(Box::new)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -404,7 +468,13 @@ impl ConfigGet for Config {
|
|||
Ok(ron::from_str(&data)?)
|
||||
}
|
||||
|
||||
_ => Err(Error::NotFound),
|
||||
_ => {
|
||||
if let Some(previous) = self.previous.as_ref() {
|
||||
previous.get_local(key)
|
||||
} else {
|
||||
Err(Error::NotFound)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ no-default = []
|
|||
|
||||
[dependencies]
|
||||
palette = { workspace = true, features = ["serializing"] }
|
||||
hex_color = { version = "3", features = ["serde"] }
|
||||
almost = "0.2"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { version = "1.0.149", optional = true, features = [
|
||||
|
|
@ -34,6 +35,3 @@ thiserror.workspace = true
|
|||
[dev-dependencies]
|
||||
insta = "1.47.2"
|
||||
|
||||
[profile.dev.package]
|
||||
insta.opt-level = 3
|
||||
similar.opt-level = 3
|
||||
|
|
|
|||
173
cosmic-theme/src/model/color.rs
Normal file
173
cosmic-theme/src/model/color.rs
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
//! Color representation and serde helpers for the Cosmic theme
|
||||
|
||||
use hex_color::HexColor;
|
||||
use palette::{Srgb, Srgba};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A color in the Cosmic theme for serialization and deserialization
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum ColorRepr {
|
||||
/// A color represented as a hex string
|
||||
#[serde(with = "hex_color::rgba")]
|
||||
Hex(HexColor),
|
||||
/// A color represented as an RGBA value
|
||||
Rgba(Srgba),
|
||||
/// A color represented as an RGB value
|
||||
Rgb(Srgb),
|
||||
}
|
||||
|
||||
/// An optional color in the Cosmic theme for serialization and deserialization
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Deserialize, Serialize)]
|
||||
#[serde(transparent)]
|
||||
pub struct ColorReprOption(Option<ColorRepr>);
|
||||
|
||||
impl From<Srgb> for ColorRepr {
|
||||
fn from(color: Srgb) -> Self {
|
||||
let rgb_u8: Srgb<u8> = color.into_format();
|
||||
ColorRepr::Hex(HexColor {
|
||||
r: rgb_u8.red,
|
||||
g: rgb_u8.green,
|
||||
b: rgb_u8.blue,
|
||||
a: 255,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Srgba> for ColorRepr {
|
||||
fn from(color: Srgba) -> Self {
|
||||
let rgba_u8: Srgba<u8> = color.into_format();
|
||||
ColorRepr::Hex(HexColor {
|
||||
r: rgba_u8.red,
|
||||
g: rgba_u8.green,
|
||||
b: rgba_u8.blue,
|
||||
a: rgba_u8.alpha,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorRepr> for Srgb {
|
||||
fn from(value: ColorRepr) -> Self {
|
||||
match value {
|
||||
ColorRepr::Hex(hex) => Srgb::<u8>::new(hex.r, hex.g, hex.b).into_format(),
|
||||
ColorRepr::Rgb(rgb) => rgb,
|
||||
ColorRepr::Rgba(rgba) => Srgb::new(rgba.red, rgba.green, rgba.blue),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorRepr> for Srgba {
|
||||
fn from(value: ColorRepr) -> Self {
|
||||
match value {
|
||||
ColorRepr::Hex(hex) => Srgba::<u8>::new(hex.r, hex.g, hex.b, hex.a).into_format(),
|
||||
ColorRepr::Rgb(rgb) => Srgba::new(rgb.red, rgb.green, rgb.blue, 1.0),
|
||||
ColorRepr::Rgba(rgba) => rgba,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorReprOption> for Option<Srgb> {
|
||||
fn from(value: ColorReprOption) -> Self {
|
||||
value.0.map(std::convert::Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ColorReprOption> for Option<Srgba> {
|
||||
fn from(value: ColorReprOption) -> Self {
|
||||
value.0.map(std::convert::Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<Srgb>> for ColorReprOption {
|
||||
fn from(value: Option<Srgb>) -> Self {
|
||||
ColorReprOption(value.map(std::convert::Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<Srgba>> for ColorReprOption {
|
||||
fn from(value: Option<Srgba>) -> Self {
|
||||
ColorReprOption(value.map(std::convert::Into::into))
|
||||
}
|
||||
}
|
||||
|
||||
/// A trait for converting between a color type and its representation for serialization and deserialization
|
||||
pub trait ConvColorRepr: Sized {
|
||||
/// Convert from a color representation to the color type
|
||||
fn from_repr(repr: ColorRepr) -> Self;
|
||||
/// Convert from the color type to its representation for serialization
|
||||
fn to_repr(&self) -> ColorRepr;
|
||||
}
|
||||
|
||||
impl ConvColorRepr for Srgba {
|
||||
fn from_repr(repr: ColorRepr) -> Self {
|
||||
repr.into()
|
||||
}
|
||||
|
||||
fn to_repr(&self) -> ColorRepr {
|
||||
(*self).into()
|
||||
}
|
||||
}
|
||||
|
||||
impl ConvColorRepr for Srgb {
|
||||
fn from_repr(repr: ColorRepr) -> Self {
|
||||
repr.into()
|
||||
}
|
||||
|
||||
fn to_repr(&self) -> ColorRepr {
|
||||
(*self).into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Serde helpers for serializing and deserializing colors in the Cosmic theme
|
||||
pub mod color_serde {
|
||||
use super::*;
|
||||
use serde::{Deserialize, Deserializer, Serializer};
|
||||
|
||||
/// Serialize a color to a hex string
|
||||
pub fn serialize<T, S>(color: &T, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: ConvColorRepr,
|
||||
S: Serializer,
|
||||
{
|
||||
let repr = color.to_repr();
|
||||
repr.serialize(serializer)
|
||||
}
|
||||
|
||||
/// Deserialize a color from a hex string or RGB/RGBA
|
||||
pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||
where
|
||||
T: ConvColorRepr,
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let repr = ColorRepr::deserialize(deserializer)?;
|
||||
Ok(T::from_repr(repr))
|
||||
}
|
||||
|
||||
/// Serde helpers for serializing and deserializing optional colors in the Cosmic theme
|
||||
pub mod option {
|
||||
use super::*;
|
||||
|
||||
/// Serialize an optional color
|
||||
pub fn serialize<T, S>(value: &Option<T>, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
T: ConvColorRepr,
|
||||
S: Serializer,
|
||||
{
|
||||
match value {
|
||||
Some(v) => 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::color::color_serde;
|
||||
use palette::Srgba;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::LazyLock;
|
||||
|
|
@ -95,75 +96,107 @@ pub struct CosmicPaletteInner {
|
|||
|
||||
/// Utility Colors
|
||||
/// Colors used for various points of emphasis in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub bright_red: Srgba,
|
||||
/// Colors used for various points of emphasis in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub bright_green: Srgba,
|
||||
/// Colors used for various points of emphasis in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub bright_orange: Srgba,
|
||||
|
||||
/// Surface Grays
|
||||
/// Colors used for three levels of surfaces in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub gray_1: Srgba,
|
||||
/// Colors used for three levels of surfaces in the UI.
|
||||
#[serde(with = "color_serde")]
|
||||
pub gray_2: Srgba,
|
||||
|
||||
/// System Neutrals
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_0: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_1: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_2: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_3: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_4: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_5: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_6: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_7: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_8: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_9: Srgba,
|
||||
/// A wider spread of dark colors for more general use.
|
||||
#[serde(with = "color_serde")]
|
||||
pub neutral_10: Srgba,
|
||||
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_blue: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_indigo: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_purple: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_pink: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_red: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_orange: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_yellow: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_green: Srgba,
|
||||
/// Potential Accent Color Combos
|
||||
#[serde(with = "color_serde")]
|
||||
pub accent_warm_grey: Srgba,
|
||||
|
||||
/// Extended Color Palette
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_warm_grey: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_orange: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_yellow: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_blue: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_purple: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_pink: Srgba,
|
||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||
#[serde(with = "color_serde")]
|
||||
pub ext_indigo: Srgba,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Dark((name:"cosmic-dark",bright_red:(red:1.0,green:0.62745098,blue:0.60392157,alpha:1.0),bright_green:(red:0.36862745,green:0.85882352,blue:0.54901960,alpha:1.0),bright_orange:(red:1.0,green:0.63921569,blue:0.49019608,alpha:1.0),gray_1:(red:0.10588235,green:0.10588235,blue:0.10588235,alpha:1.0),gray_2:(red:0.14901961,green:0.14901961,blue:0.14901961,alpha:1.0),neutral_0:(red:0.0,green:0.0,blue:0.0,alpha:1.0),neutral_1:(red:0.01176471,green:0.01176471,blue:0.01176471,alpha:1.0),neutral_2:(red:0.08627451,green:0.08627451,blue:0.08627451,alpha:1.0),neutral_3:(red:0.18039216,green:0.18039216,blue:0.18039216,alpha:1.0),neutral_4:(red:0.28235294,green:0.28235294,blue:0.28235294,alpha:1.0),neutral_5:(red:0.38823529,green:0.38823529,blue:0.38823529,alpha:1.0),neutral_6:(red:0.50196078,green:0.50196078,blue:0.50196078,alpha:1.0),neutral_7:(red:0.61960784,green:0.61960784,blue:0.61960784,alpha:1.0),neutral_8:(red:0.74509804,green:0.74509804,blue:0.74509804,alpha:1.0),neutral_9:(red:0.87058824,green:0.87058824,blue:0.87058824,alpha:1.0),neutral_10:(red:1.0,green:1.0,blue:1.0,alpha:1.0),accent_blue:(red:0.3882353,green:0.81568627,blue:0.87450981,alpha:1.0),accent_indigo:(red:0.63137255,green:0.75294118,blue:0.92156863,alpha:1.0),accent_purple:(red:0.90588235,green:0.61176471,blue:0.99607843,alpha:1.0),accent_pink:(red:1.0,green:0.61176471,blue:0.69411765,alpha:1.0),accent_red:(red:0.99215686,green:0.63137255,blue:0.62745098,alpha:1.0),accent_orange:(red:1.0,green:0.67843137,blue:0.0,alpha:1.0),accent_yellow:(red:0.96862745,green:0.87843137,blue:0.38431373,alpha:1.0),accent_green:(red:0.57254902,green:0.81176471,blue:0.61176471,alpha:1.0),accent_warm_grey:(red:0.79215686,green:0.72941176,blue:0.70588235,alpha:1.0),ext_warm_grey:(red:0.60784314,green:0.55686275,blue:0.54117647,alpha:1.0),ext_orange:(red:1.0,green:0.67843137,blue:0.0,alpha:1.0),ext_yellow:(red:0.99607843,green:0.85882353,blue:0.25098039,alpha:1.0),ext_blue:(red:0.28235294,green:0.72549020,blue:0.78039216,alpha:1.0),ext_purple:(red:0.81176471,green:0.49019608,blue:1.0,alpha:1.0),ext_pink:(red:0.97647059,green:0.22745098,blue:0.51372549,alpha:1.0),ext_indigo:(red:0.24313725,green:0.53333333,blue:1.0,alpha:1.0)))
|
||||
Dark((name: "cosmic-dark",bright_red: "#FFA09AFF",bright_green: "#5EDB8CFF",bright_orange: "#FFA37DFF",gray_1: "#1B1B1BFF",gray_2: "#262626FF",neutral_0: "#000000FF",neutral_1: "#030303FF",neutral_2: "#161616FF",neutral_3: "#2E2E2EFF",neutral_4: "#484848FF",neutral_5: "#636363FF",neutral_6: "#808080FF",neutral_7: "#9E9E9EFF",neutral_8: "#BEBEBEFF",neutral_9: "#DEDEDEFF",neutral_10: "#FFFFFFFF",accent_blue: "#63D0DFFF",accent_indigo: "#A1C0EBFF",accent_purple: "#E79CFEFF",accent_pink: "#FF9CB1FF",accent_red: "#FDA1A0FF",accent_orange: "#FFAD00FF",accent_yellow: "#F7E062FF",accent_green: "#92CF9CFF",accent_warm_grey: "#CABAB4FF",ext_warm_grey: "#9B8E8AFF",ext_orange: "#FFAD00FF",ext_yellow: "#FEDB40FF",ext_blue: "#48B9C7FF",ext_purple: "#CF7DFFFF",ext_pink: "#F93A83FF",ext_indigo: "#3E88FFFF",))
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use crate::color::color_serde;
|
||||
use palette::{Srgba, WithAlpha};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -8,14 +9,18 @@ use crate::composite::over;
|
|||
#[must_use]
|
||||
pub struct Container {
|
||||
/// the color of the container
|
||||
#[serde(with = "color_serde")]
|
||||
pub base: Srgba,
|
||||
/// the color of components in the container
|
||||
pub component: Component,
|
||||
/// the color of dividers in the container
|
||||
#[serde(with = "color_serde")]
|
||||
pub divider: Srgba,
|
||||
/// the color of text in the container
|
||||
#[serde(with = "color_serde")]
|
||||
pub on: Srgba,
|
||||
/// the color of @small_widget_container
|
||||
#[serde(with = "color_serde")]
|
||||
pub small_widget: Srgba,
|
||||
}
|
||||
|
||||
|
|
@ -45,30 +50,42 @@ impl Container {
|
|||
#[must_use]
|
||||
pub struct Component {
|
||||
/// The base color of the widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub base: Srgba,
|
||||
/// The color of the widget when it is hovered
|
||||
#[serde(with = "color_serde")]
|
||||
pub hover: Srgba,
|
||||
/// the color of the widget when it is pressed
|
||||
#[serde(with = "color_serde")]
|
||||
pub pressed: Srgba,
|
||||
/// the color of the widget when it is selected
|
||||
#[serde(with = "color_serde")]
|
||||
pub selected: Srgba,
|
||||
/// the color of the widget when it is selected
|
||||
#[serde(with = "color_serde")]
|
||||
pub selected_text: Srgba,
|
||||
/// the color of the widget when it is focused
|
||||
#[serde(with = "color_serde")]
|
||||
pub focus: Srgba,
|
||||
/// the color of dividers for this widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub divider: Srgba,
|
||||
/// the color of text for this widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub on: Srgba,
|
||||
// the color of text with opacity 80 for this widget
|
||||
// pub text_opacity_80: Srgba,
|
||||
/// the color of the widget when it is disabled
|
||||
#[serde(with = "color_serde")]
|
||||
pub disabled: Srgba,
|
||||
/// the color of text in the widget when it is disabled
|
||||
#[serde(with = "color_serde")]
|
||||
pub on_disabled: Srgba,
|
||||
/// the color of the border for the widget
|
||||
#[serde(with = "color_serde")]
|
||||
pub border: Srgba,
|
||||
/// the color of the border for the widget when it is disabled
|
||||
#[serde(with = "color_serde")]
|
||||
pub disabled_border: Srgba,
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Light((name:"cosmic-light",bright_red:(red:0.53725490,green:0.01568627,blue:0.09411765,alpha:1.0),bright_green:(red:0.0,green:0.34117647,blue:0.17254901,alpha:1.0),bright_orange:(red:0.47450980,green:0.17254902,blue:0.0,alpha:1.0),gray_1:(red:0.84313725,green:0.84313725,blue:0.84313725,alpha:1.0),gray_2:(red:0.89411765,green:0.89411765,blue:0.89411765,alpha:1.0),neutral_0:(red:1.0,green:1.0,blue:1.0,alpha:1.0),neutral_1:(red:0.87058824,green:0.87058824,blue:0.87058824,alpha:1.0),neutral_2:(red:0.74509804,green:0.74509804,blue:0.74509804,alpha:1.0),neutral_3:(red:0.61960784,green:0.61960784,blue:0.61960784,alpha:1.0),neutral_4:(red:0.50196078,green:0.50196078,blue:0.50196078,alpha:1.0),neutral_5:(red:0.38823529,green:0.38823529,blue:0.38823529,alpha:1.0),neutral_6:(red:0.28235294,green:0.28235294,blue:0.28235294,alpha:1.0),neutral_7:(red:0.18039216,green:0.18039216,blue:0.18039216,alpha:1.0),neutral_8:(red:0.08627451,green:0.08627451,blue:0.08627451,alpha:1.0),neutral_9:(red:0.01176471,green:0.01176471,blue:0.01176471,alpha:1.0),neutral_10:(red:0.0,green:0.0,blue:0.0,alpha:1.0),accent_blue:(red:0.0,green:0.32156863,blue:0.35294118,alpha:1.0),accent_indigo:(red:0.18039216,green:0.28627451,blue:0.42745098,alpha:1.0),accent_purple:(red:0.40784314,green:0.12941176,blue:0.48627451,alpha:1.0),accent_pink:(red:0.52549020,green:0.01568627,blue:0.22745098,alpha:1.0),accent_red:(red:0.47058824,green:0.16078431,blue:0.18039216,alpha:1.0),accent_orange:(red:0.38431373,green:0.25098039,blue:0.0,alpha:1.0),accent_yellow:(red:0.32549020,green:0.28235294,blue:0.0,alpha:1.0),accent_green:(red:0.09411765,green:0.33333333,blue:0.16078431,alpha:1.0),accent_warm_grey:(red:0.33333333,green:0.27843137,blue:0.25882353,alpha:1.0),ext_warm_grey:(red:0.60784314,green:0.55686275,blue:0.54117647,alpha:1.0),ext_orange:(red:0.98431373,green:0.72156863,blue:0.42352941,alpha:1.0),ext_yellow:(red:0.96862745,green:0.87843137,blue:0.38431373,alpha:1.0),ext_blue:(red:0.41568627,green:0.79215686,blue:0.84705882,alpha:1.0),ext_purple:(red:0.83529412,green:0.54901961,blue:1.0,alpha:1.0),ext_pink:(red:1.0,green:0.61176471,blue:0.86666667,alpha:1.0),ext_indigo:(red:0.58431373,green:0.76862745,blue:0.98823529,alpha:1.0)))
|
||||
Light((name: "cosmic-light",bright_red: "#890418FF",bright_green: "#00572CFF",bright_orange: "#792C00FF",gray_1: "#D7D7D7FF",gray_2: "#E4E4E4FF",neutral_0: "#FFFFFFFF",neutral_1: "#DEDEDEFF",neutral_2: "#BEBEBEFF",neutral_3: "#9E9E9EFF",neutral_4: "#808080FF",neutral_5: "#636363FF",neutral_6: "#484848FF",neutral_7: "#2E2E2EFF",neutral_8: "#161616FF",neutral_9: "#030303FF",neutral_10: "#000000FF",accent_blue: "#00525AFF",accent_indigo: "#2E496DFF",accent_purple: "#68217CFF",accent_pink: "#86043AFF",accent_red: "#78292EFF",accent_orange: "#624000FF",accent_yellow: "#534800FF",accent_green: "#185529FF",accent_warm_grey: "#554742FF",ext_warm_grey: "#9B8E8AFF",ext_orange: "#FBB86CFF",ext_yellow: "#F7E062FF",ext_blue: "#6ACAD8FF",ext_purple: "#D58CFFFF",ext_pink: "#FF9CDDFF",ext_indigo: "#95C4FCFF",))
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ pub use mode::*;
|
|||
pub use spacing::*;
|
||||
pub use theme::*;
|
||||
|
||||
pub mod color;
|
||||
mod corner;
|
||||
mod cosmic_palette;
|
||||
mod derivation;
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
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::{
|
||||
|
|
@ -9,6 +11,7 @@ use palette::color_difference::Wcag21RelativeContrast;
|
|||
use palette::rgb::Rgb;
|
||||
use palette::{IntoColor, Oklcha, Srgb, Srgba, WithAlpha};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::default;
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
/// ID for the current dark `ThemeBuilder` config
|
||||
|
|
@ -45,16 +48,24 @@ pub enum Layer {
|
|||
PartialEq,
|
||||
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||
)]
|
||||
#[version = 1]
|
||||
#[version = 2]
|
||||
pub struct Theme {
|
||||
/// name of the theme
|
||||
pub name: String,
|
||||
/// background element colors
|
||||
pub background: Container,
|
||||
pub(crate) background: Container,
|
||||
/// primary element colors
|
||||
pub primary: Container,
|
||||
pub(crate) primary: Container,
|
||||
/// secondary element colors
|
||||
pub secondary: Container,
|
||||
pub(crate) secondary: Container,
|
||||
/// background element colors
|
||||
pub(crate) transparent_background: Container,
|
||||
/// primary element colors
|
||||
pub(crate) transparent_primary: Container,
|
||||
/// secondary element colors
|
||||
pub(crate) transparent_secondary: Container,
|
||||
/// button component styling
|
||||
pub button: Component,
|
||||
/// accent element colors
|
||||
pub accent: Component,
|
||||
/// suggested element colors
|
||||
|
|
@ -79,8 +90,6 @@ pub struct Theme {
|
|||
pub list_button: Component,
|
||||
/// text button element colors
|
||||
pub text_button: Component,
|
||||
/// button component styling
|
||||
pub button: Component,
|
||||
/// palette
|
||||
pub palette: CosmicPaletteInner,
|
||||
/// spacing
|
||||
|
|
@ -97,16 +106,40 @@ pub struct Theme {
|
|||
pub active_hint: u32,
|
||||
/// cosmic-comp custom window hint color
|
||||
pub window_hint: Option<Srgb>,
|
||||
#[serde(default)]
|
||||
/// enables blurred transparency
|
||||
pub is_frosted: bool,
|
||||
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)]
|
||||
/// alpha map
|
||||
pub alpha_map: AlphaMap,
|
||||
/// 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>,
|
||||
}
|
||||
|
||||
|
|
@ -172,6 +205,39 @@ impl Theme {
|
|||
todo!();
|
||||
}
|
||||
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
/// get opaque or transparent background based on whether blur is active
|
||||
pub fn background(&self, transparent: bool) -> &Container {
|
||||
if transparent {
|
||||
&self.transparent_background
|
||||
} else {
|
||||
&self.background
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
/// get opaque or transparent primary based on whether blur is active
|
||||
pub fn primary(&self, transparent: bool) -> &Container {
|
||||
if transparent {
|
||||
&self.transparent_primary
|
||||
} else {
|
||||
&self.primary
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
/// get opaque or transparent secondary based on whether blur is active
|
||||
pub fn secondary(&self, transparent: bool) -> &Container {
|
||||
if transparent {
|
||||
&self.transparent_secondary
|
||||
} else {
|
||||
&self.secondary
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
#[allow(clippy::doc_markdown)]
|
||||
#[inline]
|
||||
|
|
@ -741,7 +807,7 @@ impl Theme {
|
|||
if color_scheme.trim().contains("default") || color_scheme.trim().contains("light") {
|
||||
return Self::light_default();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
Self::dark_default()
|
||||
}
|
||||
|
|
@ -750,10 +816,10 @@ impl Theme {
|
|||
pub fn preferred_theme() -> Self {
|
||||
let current_desktop = std::env::var("XDG_CURRENT_DESKTOP");
|
||||
|
||||
if let Ok(desktop) = current_desktop {
|
||||
if desktop.trim().to_lowercase().contains("gnome") {
|
||||
return Self::gtk_prefer_colorscheme();
|
||||
}
|
||||
if let Ok(desktop) = current_desktop
|
||||
&& desktop.trim().to_lowercase().contains("gnome")
|
||||
{
|
||||
return Self::gtk_prefer_colorscheme();
|
||||
}
|
||||
|
||||
Self::dark_default()
|
||||
|
|
@ -776,7 +842,7 @@ impl From<CosmicPalette> for Theme {
|
|||
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||
PartialEq,
|
||||
)]
|
||||
#[version = 1]
|
||||
#[version = 2]
|
||||
pub struct ThemeBuilder {
|
||||
/// override the palette for the builder
|
||||
pub palette: CosmicPalette,
|
||||
|
|
@ -785,31 +851,67 @@ 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 is_frosted: bool, // TODO handle
|
||||
pub frosted: BlurStrength,
|
||||
/// cosmic-comp window gaps size (outer, inner)
|
||||
pub gaps: (u32, u32),
|
||||
/// cosmic-comp active hint window outline width
|
||||
pub active_hint: u32,
|
||||
/// cosmic-comp custom window hint color
|
||||
#[serde(with = "color_serde_option")]
|
||||
#[cosmic_config_entry(with = ColorReprOption)]
|
||||
pub window_hint: Option<Srgb>,
|
||||
#[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)]
|
||||
/// alpha map
|
||||
pub alpha_map: AlphaMap,
|
||||
}
|
||||
|
||||
impl Default for ThemeBuilder {
|
||||
|
|
@ -827,11 +929,16 @@ impl Default for ThemeBuilder {
|
|||
success: Default::default(),
|
||||
warning: Default::default(),
|
||||
destructive: Default::default(),
|
||||
is_frosted: false,
|
||||
frosted: BlurStrength::default(),
|
||||
// cosmic-comp theme settings
|
||||
gaps: (0, 8),
|
||||
active_hint: 3,
|
||||
window_hint: None,
|
||||
frosted_windows: false,
|
||||
frosted_system_interface: false,
|
||||
frosted_panel: false,
|
||||
frosted_applets: false,
|
||||
alpha_map: AlphaMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -955,6 +1062,12 @@ 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 {
|
||||
|
|
@ -973,9 +1086,22 @@ impl ThemeBuilder {
|
|||
gaps,
|
||||
active_hint,
|
||||
window_hint,
|
||||
is_frosted,
|
||||
frosted,
|
||||
frosted_windows,
|
||||
frosted_system_interface,
|
||||
frosted_panel,
|
||||
frosted_applets,
|
||||
alpha_map,
|
||||
} = 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();
|
||||
|
||||
|
|
@ -1030,6 +1156,10 @@ impl ThemeBuilder {
|
|||
let step_array = steps(bg, NonZeroUsize::new(100).unwrap());
|
||||
let bg_index = color_index(bg, step_array.len());
|
||||
|
||||
let mut transparent_bg = bg;
|
||||
transparent_bg.alpha = container_alpha;
|
||||
let transparent_bg_steps_array = steps(transparent_bg, NonZeroUsize::new(100).unwrap());
|
||||
|
||||
let mut component_hovered_overlay = if bg_index < 91 {
|
||||
control_steps_array[10]
|
||||
} else {
|
||||
|
|
@ -1054,10 +1184,26 @@ 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()),
|
||||
&transparent_bg_steps_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
);
|
||||
|
||||
let primary = {
|
||||
let container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
|
||||
let mut container_bg = if let Some(primary_container_bg_color) = primary_container_bg {
|
||||
primary_container_bg_color
|
||||
} else {
|
||||
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
|
||||
|
|
@ -1087,6 +1233,7 @@ impl ThemeBuilder {
|
|||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
container_alpha,
|
||||
),
|
||||
component_hovered_overlay,
|
||||
component_pressed_overlay,
|
||||
|
|
@ -1099,6 +1246,48 @@ 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 step_array = steps(container_bg, NonZeroUsize::new(100).unwrap());
|
||||
let base_index: usize = color_index(container_bg, step_array.len());
|
||||
let component_base =
|
||||
get_surface_color(base_index, 6, &step_array, is_dark, &control_steps_array[3]);
|
||||
|
||||
Container::new(
|
||||
Component::component(
|
||||
component_base,
|
||||
accent,
|
||||
get_text(
|
||||
color_index(component_base, step_array.len()),
|
||||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
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,
|
||||
|
|
@ -1172,6 +1361,7 @@ 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,
|
||||
|
|
@ -1208,6 +1398,7 @@ impl ThemeBuilder {
|
|||
&step_array,
|
||||
&control_steps_array[8],
|
||||
text_steps_array.as_deref(),
|
||||
actual_alpha,
|
||||
),
|
||||
component_hovered_overlay,
|
||||
component_pressed_overlay,
|
||||
|
|
@ -1220,6 +1411,7 @@ 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,
|
||||
|
|
@ -1232,14 +1424,24 @@ impl ThemeBuilder {
|
|||
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,
|
||||
),
|
||||
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,
|
||||
)
|
||||
},
|
||||
button: Component::component(
|
||||
button_bg,
|
||||
accent,
|
||||
|
|
@ -1343,10 +1545,79 @@ impl ThemeBuilder {
|
|||
gaps,
|
||||
active_hint,
|
||||
window_hint,
|
||||
is_frosted,
|
||||
frosted,
|
||||
accent_text,
|
||||
control_tint: neutral_tint,
|
||||
text_tint,
|
||||
frosted_windows,
|
||||
frosted_system_interface,
|
||||
frosted_panel,
|
||||
frosted_applets,
|
||||
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,
|
||||
&transparent_bg_steps_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;
|
||||
|
|
@ -1365,3 +1636,181 @@ 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,13 @@
|
|||
use crate::composite::over;
|
||||
use crate::steps::steps;
|
||||
use crate::{Component, Theme};
|
||||
use palette::rgb::Rgba;
|
||||
use palette::{Darken, IntoColor, Lighten, Srgba, WithAlpha};
|
||||
use std::fs::{self, File};
|
||||
use std::io::{self, Write};
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::Path;
|
||||
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};
|
||||
|
||||
|
|
@ -218,6 +219,50 @@ 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
|
||||
|
|
@ -257,6 +302,20 @@ 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);
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ ForegroundPositive=0,87,44
|
|||
ForegroundVisited=0,82,90
|
||||
|
||||
[Colors:Selection]
|
||||
BackgroundAlternate=108,149,152
|
||||
BackgroundAlternate=108,149,153
|
||||
BackgroundNormal=0,82,90
|
||||
DecorationFocus=0,82,90
|
||||
DecorationHover=0,82,90
|
||||
|
|
|
|||
|
|
@ -266,6 +266,14 @@ 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);
|
||||
|
|
@ -291,6 +299,13 @@ 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)?;
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ 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 {
|
||||
|
|
@ -90,11 +91,28 @@ 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, 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 });
|
||||
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 });
|
||||
|
||||
*step_array.get(index).unwrap_or(fallback)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
open = "5.3.3"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../../"
|
||||
features = [
|
||||
"debug",
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ tracing = "0.1"
|
|||
env_logger = "0.10.2"
|
||||
log = "0.4.29"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../../"
|
||||
default-features = false
|
||||
features = ["applet-token"]
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ 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);
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ edition = "2021"
|
|||
|
||||
[features]
|
||||
default = ["wayland"]
|
||||
wayland = ["libcosmic/wayland"]
|
||||
wayland = ["libcosmic-yoda/wayland"]
|
||||
|
||||
[dependencies]
|
||||
env_logger = "0.11"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../../"
|
||||
features = [
|
||||
"debug",
|
||||
|
|
@ -22,4 +22,5 @@ features = [
|
|||
"surface-message",
|
||||
"multi-window",
|
||||
"wgpu",
|
||||
"wayland",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use cosmic::app::Settings;
|
|||
use cosmic::iced::{Alignment, Length, Size};
|
||||
use cosmic::prelude::*;
|
||||
use cosmic::widget::menu::{self, KeyBind};
|
||||
use cosmic::widget::nav_bar;
|
||||
use cosmic::widget::{nav_bar, RcElementWrapper};
|
||||
use cosmic::{executor, iced, widget, Core};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::LazyLock;
|
||||
|
|
@ -43,7 +43,7 @@ pub enum Action {
|
|||
impl widget::menu::Action for Action {
|
||||
type Message = Message;
|
||||
|
||||
fn message(&self) -> Message {
|
||||
fn message(&self) -> Self::Message {
|
||||
match self {
|
||||
Action::Hi => Message::Hi,
|
||||
Action::Hi2 => Message::Hi2,
|
||||
|
|
@ -52,6 +52,25 @@ 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>> {
|
||||
|
|
@ -84,6 +103,7 @@ pub enum Message {
|
|||
Hi2,
|
||||
Hi3,
|
||||
Tick,
|
||||
ValueChanged(f32),
|
||||
}
|
||||
|
||||
/// The [`App`] stores application-specific state.
|
||||
|
|
@ -95,6 +115,7 @@ pub struct App {
|
|||
hidden: bool,
|
||||
keybinds: HashMap<KeyBind, Action>,
|
||||
progress: f32,
|
||||
progress_slider: f32,
|
||||
}
|
||||
|
||||
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
||||
|
|
@ -137,6 +158,7 @@ impl cosmic::Application for App {
|
|||
hidden: true,
|
||||
keybinds: HashMap::new(),
|
||||
progress: 0.0,
|
||||
progress_slider: 0.0,
|
||||
};
|
||||
|
||||
let command = app.update_title();
|
||||
|
|
@ -155,6 +177,70 @@ 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 {
|
||||
|
|
@ -185,6 +271,9 @@ impl cosmic::Application for App {
|
|||
Message::Tick => {
|
||||
self.progress = (self.progress + 0.01) % 1.0;
|
||||
}
|
||||
Message::ValueChanged(value) => {
|
||||
self.progress_slider = value;
|
||||
}
|
||||
}
|
||||
Task::none()
|
||||
}
|
||||
|
|
@ -201,7 +290,7 @@ impl cosmic::Application for App {
|
|||
.map_or("No page selected", String::as_str);
|
||||
|
||||
let centered = widget::container(
|
||||
widget::column::with_capacity(14)
|
||||
widget::column::with_capacity(16)
|
||||
.push(widget::text::body(page_content))
|
||||
.push(
|
||||
widget::text_input::text_input("", &self.input_1)
|
||||
|
|
@ -240,6 +329,16 @@ impl cosmic::Application for App {
|
|||
widget::progress_bar::linear::Linear::new()
|
||||
.girth(10.0)
|
||||
.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),
|
||||
|
|
@ -266,6 +365,7 @@ 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]
|
||||
[dependencies.libcosmic-yoda]
|
||||
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]
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../../"
|
||||
features = [
|
||||
"debug",
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ publish = false
|
|||
[dependencies]
|
||||
apply = "0.3.0"
|
||||
fraction = "0.15.3"
|
||||
libcosmic = { path = "../..", features = [
|
||||
libcosmic-yoda = { path = "../..", features = [
|
||||
"debug",
|
||||
"winit",
|
||||
"tokio",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ edition = "2021"
|
|||
tracing = "0.1.44"
|
||||
tracing-subscriber = "0.3.22"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
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]
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
libcosmic = { path = "../..", features = ["debug", "winit", "tokio", "single-instance", "wgpu", "wayland"] }
|
||||
libcosmic-yoda = { path = "../..", features = ["debug", "winit", "tokio", "single-instance", "wgpu", "wayland"] }
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ tracing = "0.1.44"
|
|||
tracing-subscriber = "0.3.22"
|
||||
tracing-log = "0.2.0"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
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};
|
||||
use cosmic::widget::{menu, nav_bar, space};
|
||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
|
|
@ -127,18 +127,20 @@ impl cosmic::Application for App {
|
|||
Some(&self.nav_model)
|
||||
}
|
||||
|
||||
/// 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(
|
||||
/// The context menu list.
|
||||
fn nav_context_menu(&self) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
||||
Some(menu::nav_context(
|
||||
&HashMap::new(),
|
||||
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)),
|
||||
],
|
||||
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(),
|
||||
))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ edition = "2021"
|
|||
|
||||
[features]
|
||||
default = ["xdg-portal"]
|
||||
rfd = ["libcosmic/rfd"]
|
||||
xdg-portal = ["libcosmic/xdg-portal"]
|
||||
rfd = ["libcosmic-yoda/rfd"]
|
||||
xdg-portal = ["libcosmic-yoda/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]
|
||||
[dependencies.libcosmic-yoda]
|
||||
features = ["debug", "winit", "wgpu", "wayland", "tokio"]
|
||||
path = "../../"
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
|||
[dependencies]
|
||||
fraction = "0.15.3"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
||||
path = "../.."
|
||||
default-features = false
|
||||
|
|
|
|||
|
|
@ -5,6 +5,6 @@ edition = "2024"
|
|||
|
||||
[dependencies]
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "wgpu", "tokio", "xdg-portal"]
|
||||
|
|
|
|||
|
|
@ -9,6 +9,6 @@ tracing-subscriber = "0.3.22"
|
|||
tracing-log = "0.2.0"
|
||||
chrono = "*"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
||||
path = "../.."
|
||||
|
|
|
|||
|
|
@ -8,6 +8,6 @@ tracing = "0.1.44"
|
|||
tracing-subscriber = "0.3.22"
|
||||
tracing-log = "0.2.0"
|
||||
|
||||
[dependencies.libcosmic]
|
||||
[dependencies.libcosmic-yoda]
|
||||
path = "../../"
|
||||
features = ["debug", "winit", "wgpu", "tokio", "xdg-portal"]
|
||||
|
|
|
|||
34
i18n/kmr/libcosmic_yoda.ftl
Normal file
34
i18n/kmr/libcosmic_yoda.ftl
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
february = Reşemî { $year }
|
||||
close = Bigire
|
||||
documenters = Pêlbendvan
|
||||
november = Mijdar { $year }
|
||||
may = Gulan { $year }
|
||||
april = Avrêl { $year }
|
||||
translators = Wergêr
|
||||
artists = Hunermend
|
||||
license = Lîsans
|
||||
december = Berfanbar { $year }
|
||||
links = Girêdan
|
||||
march = Adar { $year }
|
||||
june = Pûşber { $year }
|
||||
august = Tebax { $year }
|
||||
developers = Pêşvebir
|
||||
july = Tîrmeh { $year }
|
||||
september = Îlon { $year }
|
||||
designers = Nigarvan
|
||||
october = Cotmeh { $year }
|
||||
january = Çile { $year }
|
||||
monday = Duşem
|
||||
mon = Duş
|
||||
tuesday = Sêşem
|
||||
tue = Sêş
|
||||
wednesday = Çarşem
|
||||
wed = Çar
|
||||
thursday = Pênçşem
|
||||
thu = Pên
|
||||
friday = Înî
|
||||
fri = Înî
|
||||
saturday = Şemî
|
||||
sat = Şem
|
||||
sunday = Yekşem
|
||||
sun = Yek
|
||||
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