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
58
.github/workflows/ci.yml
vendored
58
.github/workflows/ci.yml
vendored
|
|
@ -6,26 +6,20 @@ on:
|
||||||
- master
|
- master
|
||||||
pull_request:
|
pull_request:
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
format:
|
format:
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
- name: Rust toolchain
|
- name: Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
|
toolchain: nightly
|
||||||
components: rustfmt
|
components: rustfmt
|
||||||
- name: Cargo cache
|
rustflags: ""
|
||||||
uses: actions/cache@v3
|
|
||||||
with:
|
|
||||||
path: |
|
|
||||||
~/.cargo/registry
|
|
||||||
~/.cargo/git
|
|
||||||
key: ${{ runner.os }}-cargo-rust_stable-${{ hashFiles('**/Cargo.toml') }}
|
|
||||||
- name: Format
|
- name: Format
|
||||||
run: cargo fmt -- --check
|
run: cargo +nightly fmt -- --check
|
||||||
|
|
||||||
tests:
|
tests:
|
||||||
needs:
|
needs:
|
||||||
|
|
@ -44,28 +38,18 @@ jobs:
|
||||||
- --no-default-features --features "desktop,smol"
|
- --no-default-features --features "desktop,smol"
|
||||||
- --no-default-features --features "desktop,tokio"
|
- --no-default-features --features "desktop,tokio"
|
||||||
- -p cosmic-theme
|
- -p cosmic-theme
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
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
|
- name: System dependencies
|
||||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||||
- name: Rust toolchain
|
- name: Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
rustflags: ""
|
||||||
- name: Test features
|
- name: Test features
|
||||||
run: cargo test ${{ matrix.test_args }} -- --test-threads=1
|
run: cargo test ${{ matrix.test_args }} -- --test-threads=1
|
||||||
env:
|
env:
|
||||||
|
|
@ -82,28 +66,18 @@ jobs:
|
||||||
- "open-dialog"
|
- "open-dialog"
|
||||||
- "context-menu"
|
- "context-menu"
|
||||||
- "nav-context"
|
- "nav-context"
|
||||||
runs-on: ubuntu-22.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
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
|
- name: System dependencies
|
||||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||||
- name: Rust toolchain
|
- name: Rust toolchain
|
||||||
uses: dtolnay/rust-toolchain@stable
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
|
with:
|
||||||
|
rustflags: ""
|
||||||
- name: Check example
|
- name: Check example
|
||||||
run: cargo check -p "${{ matrix.examples }}"
|
run: cargo check -p "${{ matrix.examples }}"
|
||||||
env:
|
env:
|
||||||
|
|
|
||||||
7
.github/workflows/pages.yml
vendored
7
.github/workflows/pages.yml
vendored
|
|
@ -11,13 +11,14 @@ jobs:
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout sources
|
- name: Checkout sources
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v6
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
- name: Install Rust nightly
|
- name: Install Rust nightly
|
||||||
uses: dtolnay/rust-toolchain@master
|
uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: nightly-2026-04-27
|
toolchain: nightly-2026-04-27
|
||||||
|
rustflags: ""
|
||||||
- name: System dependencies
|
- name: System dependencies
|
||||||
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
run: sudo apt-get update; sudo apt-get install -y libxkbcommon-dev libwayland-dev
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
|
|
@ -29,7 +30,7 @@ jobs:
|
||||||
-p libcosmic \
|
-p libcosmic \
|
||||||
--verbose --features tokio,winit,wayland,desktop,single-instance,applet,xdg-portal,multi-window
|
--verbose --features tokio,winit,wayland,desktop,single-instance,applet,xdg-portal,multi-window
|
||||||
- name: Deploy documentation
|
- name: Deploy documentation
|
||||||
uses: peaceiris/actions-gh-pages@v3
|
uses: peaceiris/actions-gh-pages@v4
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
publish_dir: ./target/doc
|
publish_dir: ./target/doc
|
||||||
|
|
|
||||||
100
Cargo.toml
100
Cargo.toml
|
|
@ -1,6 +1,11 @@
|
||||||
[package]
|
[package]
|
||||||
name = "libcosmic"
|
# Yoda fork: hard-renamed. Every consumer (leyoda/cosmic-files fork + each
|
||||||
version = "1.0.0"
|
# 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"
|
edition = "2024"
|
||||||
rust-version = "1.93"
|
rust-version = "1.93"
|
||||||
|
|
||||||
|
|
@ -9,12 +14,10 @@ name = "cosmic"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = [
|
default = [
|
||||||
"winit",
|
|
||||||
"tokio",
|
"tokio",
|
||||||
"a11y",
|
"a11y",
|
||||||
"dbus-config",
|
"dbus-config",
|
||||||
"x11",
|
"wayland",
|
||||||
"iced-wayland",
|
|
||||||
"multi-window",
|
"multi-window",
|
||||||
]
|
]
|
||||||
advanced-shaping = ["iced/advanced-shaping"]
|
advanced-shaping = ["iced/advanced-shaping"]
|
||||||
|
|
@ -35,7 +38,6 @@ animated-image = [
|
||||||
autosize = []
|
autosize = []
|
||||||
applet = [
|
applet = [
|
||||||
"autosize",
|
"autosize",
|
||||||
"winit",
|
|
||||||
"wayland",
|
"wayland",
|
||||||
"tokio",
|
"tokio",
|
||||||
"cosmic-panel-config",
|
"cosmic-panel-config",
|
||||||
|
|
@ -69,7 +71,7 @@ desktop-systemd-scope = ["desktop", "dep:zbus"]
|
||||||
# Enables keycode serialization
|
# Enables keycode serialization
|
||||||
serde-keycode = ["iced_core/serde"]
|
serde-keycode = ["iced_core/serde"]
|
||||||
# Prevents multiple separate process instances.
|
# 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 async runtime
|
||||||
smol = ["dep:smol", "iced/smol", "zbus?/async-io", "rfd?/async-std"]
|
smol = ["dep:smol", "iced/smol", "zbus?/async-io", "rfd?/async-std"]
|
||||||
tokio = [
|
tokio = [
|
||||||
|
|
@ -81,32 +83,34 @@ tokio = [
|
||||||
"cosmic-config/tokio",
|
"cosmic-config/tokio",
|
||||||
]
|
]
|
||||||
# Tokio async runtime
|
# Tokio async runtime
|
||||||
# Wayland window support
|
# Wayland window support (yoda fork is Wayland-only; always active in default).
|
||||||
iced-wayland = [
|
# 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",
|
"ashpd?/wayland",
|
||||||
"autosize",
|
"autosize",
|
||||||
|
"iced/winit",
|
||||||
"iced/wayland",
|
"iced/wayland",
|
||||||
"iced_winit/wayland",
|
"iced_winit/wayland",
|
||||||
"surface-message",
|
|
||||||
]
|
|
||||||
wayland = [
|
|
||||||
"iced-wayland",
|
|
||||||
"iced_runtime/cctk",
|
"iced_runtime/cctk",
|
||||||
"iced_winit/cctk",
|
"iced_winit/cctk",
|
||||||
"iced_wgpu/cctk",
|
"iced_wgpu/cctk",
|
||||||
"iced/cctk",
|
"iced/cctk",
|
||||||
|
"dep:iced_winit",
|
||||||
"dep:cctk",
|
"dep:cctk",
|
||||||
|
"surface-message",
|
||||||
]
|
]
|
||||||
surface-message = []
|
surface-message = []
|
||||||
# multi-window support
|
# multi-window support
|
||||||
multi-window = []
|
multi-window = []
|
||||||
# Render with wgpu
|
# Render with wgpu
|
||||||
wgpu = ["iced/wgpu", "iced_wgpu"]
|
wgpu = ["iced/wgpu", "iced_wgpu"]
|
||||||
# X11 window support via winit
|
# Compat stubs — kept empty so upstream deps (cosmic-files, cosmic-text, …)
|
||||||
winit = ["iced/winit", "iced_winit"]
|
# that still ask for `winit` / `x11` features resolve cleanly against the
|
||||||
winit_debug = ["winit", "debug"]
|
# yoda fork. Activating them has no effect: no code is gated on these.
|
||||||
winit_tokio = ["winit", "tokio"]
|
winit = []
|
||||||
winit_wgpu = ["winit", "wgpu"]
|
x11 = []
|
||||||
# Enables XDG portal integrations
|
# Enables XDG portal integrations
|
||||||
xdg-portal = ["ashpd"]
|
xdg-portal = ["ashpd"]
|
||||||
qr_code = ["iced/qr_code"]
|
qr_code = ["iced/qr_code"]
|
||||||
|
|
@ -119,7 +123,6 @@ async-std = [
|
||||||
"zbus?/async-io",
|
"zbus?/async-io",
|
||||||
"iced/async-std",
|
"iced/async-std",
|
||||||
]
|
]
|
||||||
x11 = ["iced/x11", "iced_winit/x11"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
apply = "0.3.0"
|
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-fs = { version = "2.2", optional = true }
|
||||||
async-std = { workspace = true, optional = true }
|
async-std = { workspace = true, optional = true }
|
||||||
auto_enums = "0.8.8"
|
auto_enums = "0.8.8"
|
||||||
cctk = { git = "https://github.com/pop-os/cosmic-protocols", package = "cosmic-client-toolkit", rev = "c253ec1", optional = true }
|
cctk = { path = "../cosmic-protocols/client-toolkit", package = "cosmic-client-toolkit", optional = true }
|
||||||
jiff = "0.2"
|
jiff = "0.2"
|
||||||
cosmic-config = { path = "cosmic-config" }
|
cosmic-config = { path = "cosmic-config" }
|
||||||
cosmic-settings-config = { git = "https://github.com/pop-os/cosmic-settings-daemon", optional = true }
|
cosmic-settings-config = { path = "../cosmic-settings-daemon/config", optional = true }
|
||||||
# Internationalization
|
# Internationalization
|
||||||
i18n-embed = { version = "0.16.0", features = [
|
i18n-embed = { version = "0.16.0", features = [
|
||||||
"fluent-system",
|
"fluent-system",
|
||||||
|
|
@ -170,15 +173,16 @@ url = "2.5.8"
|
||||||
zbus = { workspace = true, optional = true }
|
zbus = { workspace = true, optional = true }
|
||||||
float-cmp = "0.10.0"
|
float-cmp = "0.10.0"
|
||||||
ron = { workspace = true, optional = true }
|
ron = { workspace = true, optional = true }
|
||||||
|
enumflags2 = "0.7.12"
|
||||||
|
|
||||||
# Enable DBus feature on Linux targets
|
# Enable DBus feature on Linux targets
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
cosmic-config = { path = "cosmic-config", features = ["dbus"] }
|
cosmic-config = { path = "cosmic-config", features = ["dbus"] }
|
||||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings" }
|
cosmic-settings-daemon = { path = "../dbus-settings-bindings/cosmic-settings-daemon" }
|
||||||
zbus.workspace = true
|
zbus = { version = "5.14.0", default-features = false }
|
||||||
|
|
||||||
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
|
||||||
freedesktop-icons = { package = "cosmic-freedesktop-icons", git = "https://github.com/pop-os/freedesktop-icons" }
|
freedesktop-icons = { package = "cosmic-freedesktop-icons", path = "../cosmic-freedesktop-icons" }
|
||||||
freedesktop-desktop-entry = { version = "0.8.1", optional = true }
|
freedesktop-desktop-entry = { version = "0.8.1", optional = true }
|
||||||
shlex = { version = "1.3.0", optional = true }
|
shlex = { version = "1.3.0", optional = true }
|
||||||
|
|
||||||
|
|
@ -224,28 +228,58 @@ optional = true
|
||||||
|
|
||||||
[dependencies.iced_tiny_skia]
|
[dependencies.iced_tiny_skia]
|
||||||
path = "./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]
|
[dependencies.iced_winit]
|
||||||
path = "./iced/winit"
|
path = "./iced/winit"
|
||||||
optional = true
|
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]
|
[dependencies.iced_wgpu]
|
||||||
path = "./iced/wgpu"
|
path = "./iced/wgpu"
|
||||||
optional = true
|
optional = true
|
||||||
|
|
||||||
[dependencies.cosmic-panel-config]
|
[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
|
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]
|
[workspace]
|
||||||
members = [
|
members = [
|
||||||
"cosmic-config",
|
"cosmic-config",
|
||||||
"cosmic-config-derive",
|
"cosmic-config-derive",
|
||||||
"cosmic-theme",
|
"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]
|
[workspace.dependencies]
|
||||||
async-std = "1.13"
|
async-std = "1.13"
|
||||||
|
|
@ -256,7 +290,15 @@ serde = "1.0"
|
||||||
thiserror = "2.0"
|
thiserror = "2.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tokio = "1.52"
|
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]
|
[dev-dependencies]
|
||||||
tempfile = "3.27.0"
|
tempfile = "3.27.0"
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,32 @@
|
||||||
use proc_macro::TokenStream;
|
use proc_macro::TokenStream;
|
||||||
use quote::quote;
|
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 {
|
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();
|
let ast = syn::parse(input).unwrap();
|
||||||
|
|
||||||
// Build the trait implementation
|
|
||||||
impl_cosmic_config_entry_macro(&ast)
|
impl_cosmic_config_entry_macro(&ast)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_cosmic_config_attrs(field: &syn::Field) -> Result<Option<syn::Type>, syn::Error> {
|
||||||
|
let mut with = None;
|
||||||
|
|
||||||
|
for attr in &field.attrs {
|
||||||
|
if !attr.path().is_ident("cosmic_config_entry") {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
attr.parse_nested_meta(|meta| {
|
||||||
|
if meta.path.is_ident("with") {
|
||||||
|
let value = meta.value()?;
|
||||||
|
with = Some(value.parse()?);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(with)
|
||||||
|
}
|
||||||
|
|
||||||
fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
let attributes = &ast.attrs;
|
let attributes = &ast.attrs;
|
||||||
let version = attributes
|
let version = attributes
|
||||||
|
|
@ -48,14 +63,48 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
|
|
||||||
let write_each_config_field = fields.iter().map(|field| {
|
let write_each_config_field = fields.iter().map(|field| {
|
||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
|
let with = match get_cosmic_config_attrs(field) {
|
||||||
|
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! {
|
quote! {
|
||||||
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
|
cosmic_config::ConfigSet::set(&tx, stringify!(#field_name), &self.#field_name)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let get_each_config_field = fields.iter().map(|field| {
|
let get_each_config_field = fields.iter().map(|field| {
|
||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
let field_type = &field.ty;
|
let field_type = &field.ty;
|
||||||
|
let with = match get_cosmic_config_attrs(field) {
|
||||||
|
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! {
|
quote! {
|
||||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||||
Ok(#field_name) => default.#field_name = #field_name,
|
Ok(#field_name) => default.#field_name = #field_name,
|
||||||
|
|
@ -63,11 +112,35 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
Err(e) => errors.push(e),
|
Err(e) => errors.push(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let update_each_config_field = fields.iter().map(|field| {
|
let update_each_config_field = fields.iter().map(|field| {
|
||||||
let field_name = &field.ident;
|
let field_name = &field.ident;
|
||||||
let field_type = &field.ty;
|
let field_type = &field.ty;
|
||||||
|
let with = match get_cosmic_config_attrs(field) {
|
||||||
|
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! {
|
quote! {
|
||||||
stringify!(#field_name) => {
|
stringify!(#field_name) => {
|
||||||
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
match cosmic_config::ConfigGet::get::<#field_type>(config, stringify!(#field_name)) {
|
||||||
|
|
@ -77,8 +150,7 @@ fn impl_cosmic_config_entry_macro(ast: &syn::DeriveInput) -> TokenStream {
|
||||||
}
|
}
|
||||||
self.#field_name = value;
|
self.#field_name = value;
|
||||||
},
|
},
|
||||||
Err(e) => {
|
Err(e) => errors.push(e),
|
||||||
errors.push(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,8 +10,8 @@ macro = ["cosmic-config-derive"]
|
||||||
subscription = ["iced_futures"]
|
subscription = ["iced_futures"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cosmic-settings-daemon = { git = "https://github.com/pop-os/dbus-settings-bindings", optional = true }
|
cosmic-settings-daemon = { path = "../../dbus-settings-bindings/cosmic-settings-daemon", optional = true }
|
||||||
zbus = { workspace = true, default-features = false, optional = true }
|
zbus = { version = "5.14.0", default-features = false, optional = true }
|
||||||
atomicwrites = { git = "https://github.com/jackpot51/rust-atomicwrites" }
|
atomicwrites = { git = "https://github.com/jackpot51/rust-atomicwrites" }
|
||||||
calloop = { version = "0.14.4", optional = true }
|
calloop = { version = "0.14.4", optional = true }
|
||||||
notify = "8.2.0"
|
notify = "8.2.0"
|
||||||
|
|
|
||||||
|
|
@ -175,6 +175,7 @@ pub trait ConfigSet {
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
system_path: Option<PathBuf>,
|
system_path: Option<PathBuf>,
|
||||||
user_path: Option<PathBuf>,
|
user_path: Option<PathBuf>,
|
||||||
|
previous: Option<Box<Config>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check that the name is relative and doesn't contain . or ..
|
/// Check that the name is relative and doesn't contain . or ..
|
||||||
|
|
@ -193,9 +194,13 @@ fn sanitize_name(name: &str) -> Result<&Path, Error> {
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Get a system config for the given name and config version
|
/// Get a system config for the given name and config version
|
||||||
pub fn system(name: &str, version: u64) -> Result<Self, Error> {
|
pub fn system(name: &str, version: u64) -> Result<Self, Error> {
|
||||||
|
Self::system_inner(name, version, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn system_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||||
let path = sanitize_name(name)?.join(format!("v{version}"));
|
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(path);
|
let system_path = xdg::BaseDirectories::with_prefix("cosmic").find_data_file(&path);
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
let system_path =
|
let system_path =
|
||||||
|
|
@ -205,6 +210,13 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path,
|
system_path,
|
||||||
user_path: None,
|
user_path: None,
|
||||||
|
previous: if version > 1 && look_for_previous {
|
||||||
|
Self::system_inner(name, version - 1, false)
|
||||||
|
.ok()
|
||||||
|
.map(Box::new)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -212,6 +224,10 @@ impl Config {
|
||||||
// Use folder at XDG config/name for config storage, return Config if successful
|
// Use folder at XDG config/name for config storage, return Config if successful
|
||||||
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||||
pub fn new(name: &str, version: u64) -> Result<Self, Error> {
|
pub fn new(name: &str, version: u64) -> Result<Self, Error> {
|
||||||
|
Self::new_inner(name, version, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||||
// Look for [name]/v[version]
|
// Look for [name]/v[version]
|
||||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||||
|
|
||||||
|
|
@ -236,15 +252,29 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path,
|
system_path,
|
||||||
user_path: Some(user_path),
|
user_path: Some(user_path),
|
||||||
|
previous: if version > 1 && look_for_previous {
|
||||||
|
Self::new_inner(name, version - 1, false).ok().map(Box::new)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get config for the given application name and config version and custom path.
|
/// Get config for the given application name and config version and custom path.
|
||||||
pub fn with_custom_path(name: &str, version: u64, custom_path: PathBuf) -> Result<Self, Error> {
|
pub fn with_custom_path(name: &str, version: u64, custom_path: PathBuf) -> Result<Self, Error> {
|
||||||
|
Self::with_custom_path_inner(name, version, custom_path, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn with_custom_path_inner(
|
||||||
|
name: &str,
|
||||||
|
version: u64,
|
||||||
|
custom_path: PathBuf,
|
||||||
|
look_for_previous: bool,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
// Look for [name]/v[version]
|
// Look for [name]/v[version]
|
||||||
let path = sanitize_name(name)?.join(format!("v{version}"));
|
let path = sanitize_name(name)?.join(format!("v{version}"));
|
||||||
|
|
||||||
let mut user_path = custom_path;
|
let mut user_path = custom_path.clone();
|
||||||
user_path.push("cosmic");
|
user_path.push("cosmic");
|
||||||
user_path.push(path);
|
user_path.push(path);
|
||||||
// Create new configuration directory if not found.
|
// Create new configuration directory if not found.
|
||||||
|
|
@ -254,6 +284,13 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path: None,
|
system_path: None,
|
||||||
user_path: Some(user_path),
|
user_path: Some(user_path),
|
||||||
|
previous: if version > 1 && look_for_previous {
|
||||||
|
Self::with_custom_path_inner(name, version - 1, custom_path.clone(), false)
|
||||||
|
.ok()
|
||||||
|
.map(Box::new)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,6 +300,10 @@ impl Config {
|
||||||
// Use folder at XDG config/name for config storage, return Config if successful
|
// Use folder at XDG config/name for config storage, return Config if successful
|
||||||
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
//TODO: fallbacks for flatpak (HOST_XDG_CONFIG_HOME, xdg-desktop settings proxy)
|
||||||
pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
|
pub fn new_state(name: &str, version: u64) -> Result<Self, Error> {
|
||||||
|
Self::new_state_inner(name, version, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_state_inner(name: &str, version: u64, look_for_previous: bool) -> Result<Self, Error> {
|
||||||
// Look for [name]/v[version]
|
// Look for [name]/v[version]
|
||||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||||
|
|
||||||
|
|
@ -276,11 +317,27 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path: None,
|
system_path: None,
|
||||||
user_path: Some(user_path),
|
user_path: Some(user_path),
|
||||||
|
previous: if version > 1 && look_for_previous {
|
||||||
|
Self::new_state_inner(name, version - 1, false)
|
||||||
|
.ok()
|
||||||
|
.map(Box::new)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get data for the given application name and config version.
|
/// 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> {
|
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]
|
// Look for [name]/v[version]
|
||||||
let path = sanitize_name(name)?.join(format!("v{}", version));
|
let path = sanitize_name(name)?.join(format!("v{}", version));
|
||||||
|
|
||||||
|
|
@ -294,6 +351,13 @@ impl Config {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
system_path: None,
|
system_path: None,
|
||||||
user_path: Some(user_path),
|
user_path: Some(user_path),
|
||||||
|
previous: if version > 1 && look_for_previous {
|
||||||
|
Self::new_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)?)
|
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]
|
[dependencies]
|
||||||
palette = { workspace = true, features = ["serializing"] }
|
palette = { workspace = true, features = ["serializing"] }
|
||||||
|
hex_color = { version = "3", features = ["serde"] }
|
||||||
almost = "0.2"
|
almost = "0.2"
|
||||||
serde = { workspace = true, features = ["derive"] }
|
serde = { workspace = true, features = ["derive"] }
|
||||||
serde_json = { version = "1.0.149", optional = true, features = [
|
serde_json = { version = "1.0.149", optional = true, features = [
|
||||||
|
|
@ -34,6 +35,3 @@ thiserror.workspace = true
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
insta = "1.47.2"
|
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 palette::Srgba;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
@ -95,75 +96,107 @@ pub struct CosmicPaletteInner {
|
||||||
|
|
||||||
/// Utility Colors
|
/// Utility Colors
|
||||||
/// Colors used for various points of emphasis in the UI.
|
/// Colors used for various points of emphasis in the UI.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub bright_red: Srgba,
|
pub bright_red: Srgba,
|
||||||
/// Colors used for various points of emphasis in the UI.
|
/// Colors used for various points of emphasis in the UI.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub bright_green: Srgba,
|
pub bright_green: Srgba,
|
||||||
/// Colors used for various points of emphasis in the UI.
|
/// Colors used for various points of emphasis in the UI.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub bright_orange: Srgba,
|
pub bright_orange: Srgba,
|
||||||
|
|
||||||
/// Surface Grays
|
/// Surface Grays
|
||||||
/// Colors used for three levels of surfaces in the UI.
|
/// Colors used for three levels of surfaces in the UI.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub gray_1: Srgba,
|
pub gray_1: Srgba,
|
||||||
/// Colors used for three levels of surfaces in the UI.
|
/// Colors used for three levels of surfaces in the UI.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub gray_2: Srgba,
|
pub gray_2: Srgba,
|
||||||
|
|
||||||
/// System Neutrals
|
/// System Neutrals
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_0: Srgba,
|
pub neutral_0: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_1: Srgba,
|
pub neutral_1: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_2: Srgba,
|
pub neutral_2: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_3: Srgba,
|
pub neutral_3: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_4: Srgba,
|
pub neutral_4: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_5: Srgba,
|
pub neutral_5: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_6: Srgba,
|
pub neutral_6: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_7: Srgba,
|
pub neutral_7: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_8: Srgba,
|
pub neutral_8: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_9: Srgba,
|
pub neutral_9: Srgba,
|
||||||
/// A wider spread of dark colors for more general use.
|
/// A wider spread of dark colors for more general use.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub neutral_10: Srgba,
|
pub neutral_10: Srgba,
|
||||||
|
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_blue: Srgba,
|
pub accent_blue: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_indigo: Srgba,
|
pub accent_indigo: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_purple: Srgba,
|
pub accent_purple: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_pink: Srgba,
|
pub accent_pink: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_red: Srgba,
|
pub accent_red: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_orange: Srgba,
|
pub accent_orange: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_yellow: Srgba,
|
pub accent_yellow: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_green: Srgba,
|
pub accent_green: Srgba,
|
||||||
/// Potential Accent Color Combos
|
/// Potential Accent Color Combos
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub accent_warm_grey: Srgba,
|
pub accent_warm_grey: Srgba,
|
||||||
|
|
||||||
/// Extended Color Palette
|
/// Extended Color Palette
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub ext_warm_grey: Srgba,
|
pub ext_warm_grey: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub ext_orange: Srgba,
|
pub ext_orange: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub ext_yellow: Srgba,
|
pub ext_yellow: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub ext_blue: Srgba,
|
pub ext_blue: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub ext_purple: Srgba,
|
pub ext_purple: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub ext_pink: Srgba,
|
pub ext_pink: Srgba,
|
||||||
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
/// Colors used for themes, app icons, illustrations, and other brand purposes.
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub ext_indigo: Srgba,
|
pub ext_indigo: Srgba,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Dark((name:"cosmic-dark",bright_red:(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 palette::{Srgba, WithAlpha};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
|
@ -8,14 +9,18 @@ use crate::composite::over;
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Container {
|
pub struct Container {
|
||||||
/// the color of the container
|
/// the color of the container
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub base: Srgba,
|
pub base: Srgba,
|
||||||
/// the color of components in the container
|
/// the color of components in the container
|
||||||
pub component: Component,
|
pub component: Component,
|
||||||
/// the color of dividers in the container
|
/// the color of dividers in the container
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub divider: Srgba,
|
pub divider: Srgba,
|
||||||
/// the color of text in the container
|
/// the color of text in the container
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub on: Srgba,
|
pub on: Srgba,
|
||||||
/// the color of @small_widget_container
|
/// the color of @small_widget_container
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub small_widget: Srgba,
|
pub small_widget: Srgba,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,30 +50,42 @@ impl Container {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct Component {
|
pub struct Component {
|
||||||
/// The base color of the widget
|
/// The base color of the widget
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub base: Srgba,
|
pub base: Srgba,
|
||||||
/// The color of the widget when it is hovered
|
/// The color of the widget when it is hovered
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub hover: Srgba,
|
pub hover: Srgba,
|
||||||
/// the color of the widget when it is pressed
|
/// the color of the widget when it is pressed
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub pressed: Srgba,
|
pub pressed: Srgba,
|
||||||
/// the color of the widget when it is selected
|
/// the color of the widget when it is selected
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub selected: Srgba,
|
pub selected: Srgba,
|
||||||
/// the color of the widget when it is selected
|
/// the color of the widget when it is selected
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub selected_text: Srgba,
|
pub selected_text: Srgba,
|
||||||
/// the color of the widget when it is focused
|
/// the color of the widget when it is focused
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub focus: Srgba,
|
pub focus: Srgba,
|
||||||
/// the color of dividers for this widget
|
/// the color of dividers for this widget
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub divider: Srgba,
|
pub divider: Srgba,
|
||||||
/// the color of text for this widget
|
/// the color of text for this widget
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub on: Srgba,
|
pub on: Srgba,
|
||||||
// the color of text with opacity 80 for this widget
|
// the color of text with opacity 80 for this widget
|
||||||
// pub text_opacity_80: Srgba,
|
// pub text_opacity_80: Srgba,
|
||||||
/// the color of the widget when it is disabled
|
/// the color of the widget when it is disabled
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub disabled: Srgba,
|
pub disabled: Srgba,
|
||||||
/// the color of text in the widget when it is disabled
|
/// the color of text in the widget when it is disabled
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub on_disabled: Srgba,
|
pub on_disabled: Srgba,
|
||||||
/// the color of the border for the widget
|
/// the color of the border for the widget
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub border: Srgba,
|
pub border: Srgba,
|
||||||
/// the color of the border for the widget when it is disabled
|
/// the color of the border for the widget when it is disabled
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
pub disabled_border: Srgba,
|
pub disabled_border: Srgba,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1 +1 @@
|
||||||
Light((name:"cosmic-light",bright_red:(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 spacing::*;
|
||||||
pub use theme::*;
|
pub use theme::*;
|
||||||
|
|
||||||
|
pub mod color;
|
||||||
mod corner;
|
mod corner;
|
||||||
mod cosmic_palette;
|
mod cosmic_palette;
|
||||||
mod derivation;
|
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::composite::over;
|
||||||
use crate::steps::{color_index, get_small_widget_color, get_surface_color, get_text, steps};
|
use crate::steps::{color_index, get_small_widget_color, get_surface_color, get_text, steps};
|
||||||
use crate::{
|
use crate::{
|
||||||
|
|
@ -9,6 +11,7 @@ use palette::color_difference::Wcag21RelativeContrast;
|
||||||
use palette::rgb::Rgb;
|
use palette::rgb::Rgb;
|
||||||
use palette::{IntoColor, Oklcha, Srgb, Srgba, WithAlpha};
|
use palette::{IntoColor, Oklcha, Srgb, Srgba, WithAlpha};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::default;
|
||||||
use std::num::NonZeroUsize;
|
use std::num::NonZeroUsize;
|
||||||
|
|
||||||
/// ID for the current dark `ThemeBuilder` config
|
/// ID for the current dark `ThemeBuilder` config
|
||||||
|
|
@ -45,16 +48,24 @@ pub enum Layer {
|
||||||
PartialEq,
|
PartialEq,
|
||||||
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||||
)]
|
)]
|
||||||
#[version = 1]
|
#[version = 2]
|
||||||
pub struct Theme {
|
pub struct Theme {
|
||||||
/// name of the theme
|
/// name of the theme
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// background element colors
|
/// background element colors
|
||||||
pub background: Container,
|
pub(crate) background: Container,
|
||||||
/// primary element colors
|
/// primary element colors
|
||||||
pub primary: Container,
|
pub(crate) primary: Container,
|
||||||
/// secondary element colors
|
/// 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
|
/// accent element colors
|
||||||
pub accent: Component,
|
pub accent: Component,
|
||||||
/// suggested element colors
|
/// suggested element colors
|
||||||
|
|
@ -79,8 +90,6 @@ pub struct Theme {
|
||||||
pub list_button: Component,
|
pub list_button: Component,
|
||||||
/// text button element colors
|
/// text button element colors
|
||||||
pub text_button: Component,
|
pub text_button: Component,
|
||||||
/// button component styling
|
|
||||||
pub button: Component,
|
|
||||||
/// palette
|
/// palette
|
||||||
pub palette: CosmicPaletteInner,
|
pub palette: CosmicPaletteInner,
|
||||||
/// spacing
|
/// spacing
|
||||||
|
|
@ -97,16 +106,40 @@ pub struct Theme {
|
||||||
pub active_hint: u32,
|
pub active_hint: u32,
|
||||||
/// cosmic-comp custom window hint color
|
/// cosmic-comp custom window hint color
|
||||||
pub window_hint: Option<Srgb>,
|
pub window_hint: Option<Srgb>,
|
||||||
|
#[serde(default)]
|
||||||
/// enables blurred transparency
|
/// 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
|
/// shade color for dialogs
|
||||||
|
#[serde(with = "color_serde")]
|
||||||
|
#[cosmic_config_entry(with = ColorRepr)]
|
||||||
pub shade: Srgba,
|
pub shade: Srgba,
|
||||||
/// accent text colors
|
/// accent text colors
|
||||||
/// If None, accent base color is the accent text color.
|
/// If None, accent base color is the accent text color.
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub accent_text: Option<Srgba>,
|
pub accent_text: Option<Srgba>,
|
||||||
/// control tint color
|
/// control tint color
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub control_tint: Option<Srgb>,
|
pub control_tint: Option<Srgb>,
|
||||||
/// text tint color
|
/// text tint color
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub text_tint: Option<Srgb>,
|
pub text_tint: Option<Srgb>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,6 +205,39 @@ impl Theme {
|
||||||
todo!();
|
todo!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::doc_markdown)]
|
||||||
|
#[inline]
|
||||||
|
/// get opaque or transparent background based on whether blur is active
|
||||||
|
pub fn background(&self, transparent: bool) -> &Container {
|
||||||
|
if transparent {
|
||||||
|
&self.transparent_background
|
||||||
|
} else {
|
||||||
|
&self.background
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::doc_markdown)]
|
||||||
|
#[inline]
|
||||||
|
/// get opaque or transparent primary based on whether blur is active
|
||||||
|
pub fn primary(&self, transparent: bool) -> &Container {
|
||||||
|
if transparent {
|
||||||
|
&self.transparent_primary
|
||||||
|
} else {
|
||||||
|
&self.primary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::doc_markdown)]
|
||||||
|
#[inline]
|
||||||
|
/// get opaque or transparent secondary based on whether blur is active
|
||||||
|
pub fn secondary(&self, transparent: bool) -> &Container {
|
||||||
|
if transparent {
|
||||||
|
&self.transparent_secondary
|
||||||
|
} else {
|
||||||
|
&self.secondary
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(clippy::doc_markdown)]
|
#[allow(clippy::doc_markdown)]
|
||||||
#[inline]
|
#[inline]
|
||||||
|
|
@ -741,7 +807,7 @@ impl Theme {
|
||||||
if color_scheme.trim().contains("default") || color_scheme.trim().contains("light") {
|
if color_scheme.trim().contains("default") || color_scheme.trim().contains("light") {
|
||||||
return Self::light_default();
|
return Self::light_default();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
Self::dark_default()
|
Self::dark_default()
|
||||||
}
|
}
|
||||||
|
|
@ -750,11 +816,11 @@ impl Theme {
|
||||||
pub fn preferred_theme() -> Self {
|
pub fn preferred_theme() -> Self {
|
||||||
let current_desktop = std::env::var("XDG_CURRENT_DESKTOP");
|
let current_desktop = std::env::var("XDG_CURRENT_DESKTOP");
|
||||||
|
|
||||||
if let Ok(desktop) = current_desktop {
|
if let Ok(desktop) = current_desktop
|
||||||
if desktop.trim().to_lowercase().contains("gnome") {
|
&& desktop.trim().to_lowercase().contains("gnome")
|
||||||
|
{
|
||||||
return Self::gtk_prefer_colorscheme();
|
return Self::gtk_prefer_colorscheme();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Self::dark_default()
|
Self::dark_default()
|
||||||
}
|
}
|
||||||
|
|
@ -776,7 +842,7 @@ impl From<CosmicPalette> for Theme {
|
||||||
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
cosmic_config::cosmic_config_derive::CosmicConfigEntry,
|
||||||
PartialEq,
|
PartialEq,
|
||||||
)]
|
)]
|
||||||
#[version = 1]
|
#[version = 2]
|
||||||
pub struct ThemeBuilder {
|
pub struct ThemeBuilder {
|
||||||
/// override the palette for the builder
|
/// override the palette for the builder
|
||||||
pub palette: CosmicPalette,
|
pub palette: CosmicPalette,
|
||||||
|
|
@ -785,31 +851,67 @@ pub struct ThemeBuilder {
|
||||||
/// override corner radii for the builder
|
/// override corner radii for the builder
|
||||||
pub corner_radii: CornerRadii,
|
pub corner_radii: CornerRadii,
|
||||||
/// override neutral_tint for the builder
|
/// override neutral_tint for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub neutral_tint: Option<Srgb>,
|
pub neutral_tint: Option<Srgb>,
|
||||||
/// override bg_color for the builder
|
/// override bg_color for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub bg_color: Option<Srgba>,
|
pub bg_color: Option<Srgba>,
|
||||||
/// override the primary container bg color for the builder
|
/// override the primary container bg color for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub primary_container_bg: Option<Srgba>,
|
pub primary_container_bg: Option<Srgba>,
|
||||||
/// override the secontary container bg color for the builder
|
/// override the secontary container bg color for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub secondary_container_bg: Option<Srgba>,
|
pub secondary_container_bg: Option<Srgba>,
|
||||||
/// override the text tint for the builder
|
/// override the text tint for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub text_tint: Option<Srgb>,
|
pub text_tint: Option<Srgb>,
|
||||||
/// override the accent color for the builder
|
/// override the accent color for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub accent: Option<Srgb>,
|
pub accent: Option<Srgb>,
|
||||||
/// override the success color for the builder
|
/// override the success color for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub success: Option<Srgb>,
|
pub success: Option<Srgb>,
|
||||||
/// override the warning color for the builder
|
/// override the warning color for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub warning: Option<Srgb>,
|
pub warning: Option<Srgb>,
|
||||||
/// override the destructive color for the builder
|
/// override the destructive color for the builder
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub destructive: Option<Srgb>,
|
pub destructive: Option<Srgb>,
|
||||||
|
#[serde(default)]
|
||||||
/// enabled blurred transparency
|
/// enabled blurred transparency
|
||||||
pub is_frosted: bool, // TODO handle
|
pub frosted: BlurStrength,
|
||||||
/// cosmic-comp window gaps size (outer, inner)
|
/// cosmic-comp window gaps size (outer, inner)
|
||||||
pub gaps: (u32, u32),
|
pub gaps: (u32, u32),
|
||||||
/// cosmic-comp active hint window outline width
|
/// cosmic-comp active hint window outline width
|
||||||
pub active_hint: u32,
|
pub active_hint: u32,
|
||||||
/// cosmic-comp custom window hint color
|
/// cosmic-comp custom window hint color
|
||||||
|
#[serde(with = "color_serde_option")]
|
||||||
|
#[cosmic_config_entry(with = ColorReprOption)]
|
||||||
pub window_hint: Option<Srgb>,
|
pub window_hint: Option<Srgb>,
|
||||||
|
#[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 {
|
impl Default for ThemeBuilder {
|
||||||
|
|
@ -827,11 +929,16 @@ impl Default for ThemeBuilder {
|
||||||
success: Default::default(),
|
success: Default::default(),
|
||||||
warning: Default::default(),
|
warning: Default::default(),
|
||||||
destructive: Default::default(),
|
destructive: Default::default(),
|
||||||
is_frosted: false,
|
frosted: BlurStrength::default(),
|
||||||
// cosmic-comp theme settings
|
// cosmic-comp theme settings
|
||||||
gaps: (0, 8),
|
gaps: (0, 8),
|
||||||
active_hint: 3,
|
active_hint: 3,
|
||||||
window_hint: None,
|
window_hint: None,
|
||||||
|
frosted_windows: false,
|
||||||
|
frosted_system_interface: false,
|
||||||
|
frosted_panel: false,
|
||||||
|
frosted_applets: false,
|
||||||
|
alpha_map: AlphaMap::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -955,6 +1062,12 @@ impl ThemeBuilder {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
#[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
|
/// build the theme
|
||||||
pub fn build(self) -> Theme {
|
pub fn build(self) -> Theme {
|
||||||
let Self {
|
let Self {
|
||||||
|
|
@ -973,9 +1086,22 @@ impl ThemeBuilder {
|
||||||
gaps,
|
gaps,
|
||||||
active_hint,
|
active_hint,
|
||||||
window_hint,
|
window_hint,
|
||||||
is_frosted,
|
frosted,
|
||||||
|
frosted_windows,
|
||||||
|
frosted_system_interface,
|
||||||
|
frosted_panel,
|
||||||
|
frosted_applets,
|
||||||
|
alpha_map,
|
||||||
} = self;
|
} = 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_dark = palette.is_dark();
|
||||||
let is_high_contrast = palette.is_high_contrast();
|
let is_high_contrast = palette.is_high_contrast();
|
||||||
|
|
||||||
|
|
@ -1030,6 +1156,10 @@ impl ThemeBuilder {
|
||||||
let step_array = steps(bg, NonZeroUsize::new(100).unwrap());
|
let step_array = steps(bg, NonZeroUsize::new(100).unwrap());
|
||||||
let bg_index = color_index(bg, step_array.len());
|
let bg_index = color_index(bg, step_array.len());
|
||||||
|
|
||||||
|
let mut transparent_bg = bg;
|
||||||
|
transparent_bg.alpha = container_alpha;
|
||||||
|
let transparent_bg_steps_array = steps(transparent_bg, NonZeroUsize::new(100).unwrap());
|
||||||
|
|
||||||
let mut component_hovered_overlay = if bg_index < 91 {
|
let mut component_hovered_overlay = if bg_index < 91 {
|
||||||
control_steps_array[10]
|
control_steps_array[10]
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1054,10 +1184,26 @@ impl ThemeBuilder {
|
||||||
&step_array,
|
&step_array,
|
||||||
&control_steps_array[8],
|
&control_steps_array[8],
|
||||||
text_steps_array.as_deref(),
|
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 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
|
primary_container_bg_color
|
||||||
} else {
|
} else {
|
||||||
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
|
get_surface_color(bg_index, 5, &step_array, is_dark, &control_steps_array[1])
|
||||||
|
|
@ -1087,6 +1233,7 @@ impl ThemeBuilder {
|
||||||
&step_array,
|
&step_array,
|
||||||
&control_steps_array[8],
|
&control_steps_array[8],
|
||||||
text_steps_array.as_deref(),
|
text_steps_array.as_deref(),
|
||||||
|
container_alpha,
|
||||||
),
|
),
|
||||||
component_hovered_overlay,
|
component_hovered_overlay,
|
||||||
component_pressed_overlay,
|
component_pressed_overlay,
|
||||||
|
|
@ -1099,6 +1246,48 @@ impl ThemeBuilder {
|
||||||
&step_array,
|
&step_array,
|
||||||
&control_steps_array[8],
|
&control_steps_array[8],
|
||||||
text_steps_array.as_deref(),
|
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]),
|
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||||
is_high_contrast,
|
is_high_contrast,
|
||||||
|
|
@ -1172,6 +1361,7 @@ impl ThemeBuilder {
|
||||||
&step_array,
|
&step_array,
|
||||||
&control_steps_array[8],
|
&control_steps_array[8],
|
||||||
text_steps_array.as_deref(),
|
text_steps_array.as_deref(),
|
||||||
|
actual_alpha,
|
||||||
),
|
),
|
||||||
get_small_widget_color(bg_index, 5, &neutral_steps, &control_steps_array[6]),
|
get_small_widget_color(bg_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||||
is_high_contrast,
|
is_high_contrast,
|
||||||
|
|
@ -1208,6 +1398,7 @@ impl ThemeBuilder {
|
||||||
&step_array,
|
&step_array,
|
||||||
&control_steps_array[8],
|
&control_steps_array[8],
|
||||||
text_steps_array.as_deref(),
|
text_steps_array.as_deref(),
|
||||||
|
actual_alpha,
|
||||||
),
|
),
|
||||||
component_hovered_overlay,
|
component_hovered_overlay,
|
||||||
component_pressed_overlay,
|
component_pressed_overlay,
|
||||||
|
|
@ -1220,6 +1411,7 @@ impl ThemeBuilder {
|
||||||
&step_array,
|
&step_array,
|
||||||
&control_steps_array[8],
|
&control_steps_array[8],
|
||||||
text_steps_array.as_deref(),
|
text_steps_array.as_deref(),
|
||||||
|
actual_alpha,
|
||||||
),
|
),
|
||||||
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
|
get_small_widget_color(base_index, 5, &neutral_steps, &control_steps_array[6]),
|
||||||
is_high_contrast,
|
is_high_contrast,
|
||||||
|
|
@ -1232,14 +1424,24 @@ impl ThemeBuilder {
|
||||||
button_hovered_overlay,
|
button_hovered_overlay,
|
||||||
button_pressed_overlay,
|
button_pressed_overlay,
|
||||||
),
|
),
|
||||||
accent_button: Component::colored_button(
|
accent_button: {
|
||||||
|
let step_array = steps(control_steps_array[5], NonZeroUsize::new(100).unwrap());
|
||||||
|
|
||||||
|
Component::colored_button(
|
||||||
accent,
|
accent,
|
||||||
control_steps_array[1],
|
control_steps_array[1],
|
||||||
control_steps_array[0],
|
get_text(
|
||||||
|
color_index(accent, step_array.len()),
|
||||||
|
&step_array,
|
||||||
|
&step_array[0],
|
||||||
|
text_steps_array.as_deref(),
|
||||||
|
1., // accent is opaque anyway
|
||||||
|
),
|
||||||
accent,
|
accent,
|
||||||
button_hovered_overlay,
|
button_hovered_overlay,
|
||||||
button_pressed_overlay,
|
button_pressed_overlay,
|
||||||
),
|
)
|
||||||
|
},
|
||||||
button: Component::component(
|
button: Component::component(
|
||||||
button_bg,
|
button_bg,
|
||||||
accent,
|
accent,
|
||||||
|
|
@ -1343,10 +1545,79 @@ impl ThemeBuilder {
|
||||||
gaps,
|
gaps,
|
||||||
active_hint,
|
active_hint,
|
||||||
window_hint,
|
window_hint,
|
||||||
is_frosted,
|
frosted,
|
||||||
accent_text,
|
accent_text,
|
||||||
control_tint: neutral_tint,
|
control_tint: neutral_tint,
|
||||||
text_tint,
|
text_tint,
|
||||||
|
frosted_windows,
|
||||||
|
frosted_system_interface,
|
||||||
|
frosted_panel,
|
||||||
|
frosted_applets,
|
||||||
|
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.spacing = spacing;
|
||||||
theme.corner_radii = corner_radii;
|
theme.corner_radii = corner_radii;
|
||||||
|
|
@ -1365,3 +1636,181 @@ impl ThemeBuilder {
|
||||||
Config::new(LIGHT_THEME_BUILDER_ID, Self::VERSION)
|
Config::new(LIGHT_THEME_BUILDER_ID, Self::VERSION)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Actual blur radius is decided by cosmic-comp,
|
||||||
|
/// but this represents the strength of the blur effect.
|
||||||
|
#[allow(missing_docs)]
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(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::{Component, Theme, composite::over, steps::steps};
|
||||||
use crate::steps::steps;
|
use configparser::ini::Ini;
|
||||||
use crate::{Component, Theme};
|
use palette::{Darken, IntoColor, Lighten, Srgba, WithAlpha, rgb::Rgba};
|
||||||
use palette::rgb::Rgba;
|
use std::{
|
||||||
use palette::{Darken, IntoColor, Lighten, Srgba, WithAlpha};
|
fs::{self, File},
|
||||||
use std::fs::{self, File};
|
io::{self, Write},
|
||||||
use std::io::{self, Write};
|
num::NonZeroUsize,
|
||||||
use std::num::NonZeroUsize;
|
path::Path,
|
||||||
use std::path::Path;
|
process::Command,
|
||||||
|
};
|
||||||
|
|
||||||
use super::{OutputError, to_rgba};
|
use super::{OutputError, to_rgba};
|
||||||
|
|
||||||
|
|
@ -218,6 +219,50 @@ impl Theme {
|
||||||
Ok(())
|
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
|
/// Reset the applied gtk css
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
@ -257,6 +302,20 @@ impl Theme {
|
||||||
Ok(())
|
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>> {
|
fn is_cosmic_css(path: &Path, cosmic_css: &Path) -> io::Result<Option<bool>> {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ ForegroundPositive=0,87,44
|
||||||
ForegroundVisited=0,82,90
|
ForegroundVisited=0,82,90
|
||||||
|
|
||||||
[Colors:Selection]
|
[Colors:Selection]
|
||||||
BackgroundAlternate=108,149,152
|
BackgroundAlternate=108,149,153
|
||||||
BackgroundNormal=0,82,90
|
BackgroundNormal=0,82,90
|
||||||
DecorationFocus=0,82,90
|
DecorationFocus=0,82,90
|
||||||
DecorationHover=0,82,90
|
DecorationHover=0,82,90
|
||||||
|
|
|
||||||
|
|
@ -266,6 +266,14 @@ impl From<Theme> for VsTheme {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Theme {
|
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]
|
#[cold]
|
||||||
pub fn apply_vs_code(self) -> Result<(), OutputError> {
|
pub fn apply_vs_code(self) -> Result<(), OutputError> {
|
||||||
let vs_theme = VsTheme::from(self);
|
let vs_theme = VsTheme::from(self);
|
||||||
|
|
@ -291,6 +299,13 @@ impl Theme {
|
||||||
Ok(())
|
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]
|
#[cold]
|
||||||
pub fn reset_vs_code() -> Result<(), OutputError> {
|
pub fn reset_vs_code() -> Result<(), OutputError> {
|
||||||
let mut config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?;
|
let mut config_dir = dirs::config_dir().ok_or(OutputError::MissingConfigDir)?;
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ pub fn get_text(
|
||||||
step_array: &[Srgba],
|
step_array: &[Srgba],
|
||||||
fallback: &Srgba,
|
fallback: &Srgba,
|
||||||
tint_array: Option<&[Srgba]>,
|
tint_array: Option<&[Srgba]>,
|
||||||
|
alpha: f32,
|
||||||
) -> Srgba {
|
) -> Srgba {
|
||||||
assert!(step_array.len() == 100);
|
assert!(step_array.len() == 100);
|
||||||
let step_array = if let Some(tint_array) = tint_array {
|
let step_array = if let Some(tint_array) = tint_array {
|
||||||
|
|
@ -90,10 +91,27 @@ pub fn get_text(
|
||||||
step_array
|
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 is_dark = base_index < 60;
|
||||||
|
|
||||||
let index = get_index(base_index, 70, step_array.len(), is_dark)
|
let index = get_index(
|
||||||
.or_else(|| get_index(base_index, 50, step_array.len(), is_dark))
|
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 });
|
.unwrap_or(if is_dark { 99 } else { 0 });
|
||||||
|
|
||||||
*step_array.get(index).unwrap_or(fallback)
|
*step_array.get(index).unwrap_or(fallback)
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
open = "5.3.3"
|
open = "5.3.3"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = [
|
features = [
|
||||||
"debug",
|
"debug",
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ tracing = "0.1"
|
||||||
env_logger = "0.10.2"
|
env_logger = "0.10.2"
|
||||||
log = "0.4.29"
|
log = "0.4.29"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["applet-token"]
|
features = ["applet-token"]
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,7 @@ impl cosmic::Application for Window {
|
||||||
Message::Surface(destroy_popup(id))
|
Message::Surface(destroy_popup(id))
|
||||||
} else {
|
} else {
|
||||||
Message::Surface(app_popup::<Window>(
|
Message::Surface(app_popup::<Window>(
|
||||||
|
|_| Default::default(),
|
||||||
move |state: &mut Window| {
|
move |state: &mut Window| {
|
||||||
let new_id = Id::unique();
|
let new_id = Id::unique();
|
||||||
state.popup = Some(new_id);
|
state.popup = Some(new_id);
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@ edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["wayland"]
|
default = ["wayland"]
|
||||||
wayland = ["libcosmic/wayland"]
|
wayland = ["libcosmic-yoda/wayland"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
env_logger = "0.11"
|
env_logger = "0.11"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = [
|
features = [
|
||||||
"debug",
|
"debug",
|
||||||
|
|
@ -22,4 +22,5 @@ features = [
|
||||||
"surface-message",
|
"surface-message",
|
||||||
"multi-window",
|
"multi-window",
|
||||||
"wgpu",
|
"wgpu",
|
||||||
|
"wayland",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use cosmic::app::Settings;
|
||||||
use cosmic::iced::{Alignment, Length, Size};
|
use cosmic::iced::{Alignment, Length, Size};
|
||||||
use cosmic::prelude::*;
|
use cosmic::prelude::*;
|
||||||
use cosmic::widget::menu::{self, KeyBind};
|
use cosmic::widget::menu::{self, KeyBind};
|
||||||
use cosmic::widget::nav_bar;
|
use cosmic::widget::{nav_bar, RcElementWrapper};
|
||||||
use cosmic::{executor, iced, widget, Core};
|
use cosmic::{executor, iced, widget, Core};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
|
@ -43,7 +43,7 @@ pub enum Action {
|
||||||
impl widget::menu::Action for Action {
|
impl widget::menu::Action for Action {
|
||||||
type Message = Message;
|
type Message = Message;
|
||||||
|
|
||||||
fn message(&self) -> Message {
|
fn message(&self) -> Self::Message {
|
||||||
match self {
|
match self {
|
||||||
Action::Hi => Message::Hi,
|
Action::Hi => Message::Hi,
|
||||||
Action::Hi2 => Message::Hi2,
|
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
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
|
@ -84,6 +103,7 @@ pub enum Message {
|
||||||
Hi2,
|
Hi2,
|
||||||
Hi3,
|
Hi3,
|
||||||
Tick,
|
Tick,
|
||||||
|
ValueChanged(f32),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The [`App`] stores application-specific state.
|
/// The [`App`] stores application-specific state.
|
||||||
|
|
@ -95,6 +115,7 @@ pub struct App {
|
||||||
hidden: bool,
|
hidden: bool,
|
||||||
keybinds: HashMap<KeyBind, Action>,
|
keybinds: HashMap<KeyBind, Action>,
|
||||||
progress: f32,
|
progress: f32,
|
||||||
|
progress_slider: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
/// Implement [`cosmic::Application`] to integrate with COSMIC.
|
||||||
|
|
@ -137,6 +158,7 @@ impl cosmic::Application for App {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
keybinds: HashMap::new(),
|
keybinds: HashMap::new(),
|
||||||
progress: 0.0,
|
progress: 0.0,
|
||||||
|
progress_slider: 0.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let command = app.update_title();
|
let command = app.update_title();
|
||||||
|
|
@ -155,6 +177,70 @@ impl cosmic::Application for App {
|
||||||
self.update_title()
|
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.
|
/// Handle application events here.
|
||||||
fn update(&mut self, message: Self::Message) -> cosmic::app::Task<Self::Message> {
|
fn update(&mut self, message: Self::Message) -> cosmic::app::Task<Self::Message> {
|
||||||
match message {
|
match message {
|
||||||
|
|
@ -185,6 +271,9 @@ impl cosmic::Application for App {
|
||||||
Message::Tick => {
|
Message::Tick => {
|
||||||
self.progress = (self.progress + 0.01) % 1.0;
|
self.progress = (self.progress + 0.01) % 1.0;
|
||||||
}
|
}
|
||||||
|
Message::ValueChanged(value) => {
|
||||||
|
self.progress_slider = value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
@ -201,7 +290,7 @@ impl cosmic::Application for App {
|
||||||
.map_or("No page selected", String::as_str);
|
.map_or("No page selected", String::as_str);
|
||||||
|
|
||||||
let centered = widget::container(
|
let centered = widget::container(
|
||||||
widget::column::with_capacity(14)
|
widget::column::with_capacity(16)
|
||||||
.push(widget::text::body(page_content))
|
.push(widget::text::body(page_content))
|
||||||
.push(
|
.push(
|
||||||
widget::text_input::text_input("", &self.input_1)
|
widget::text_input::text_input("", &self.input_1)
|
||||||
|
|
@ -240,6 +329,16 @@ impl cosmic::Application for App {
|
||||||
widget::progress_bar::linear::Linear::new()
|
widget::progress_bar::linear::Linear::new()
|
||||||
.girth(10.0)
|
.girth(10.0)
|
||||||
.progress(self.progress)
|
.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)
|
.width(Length::Fill)
|
||||||
.markers([0.25, 0.5, 0.75])
|
.markers([0.25, 0.5, 0.75])
|
||||||
.segment_spacing(2),
|
.segment_spacing(2),
|
||||||
|
|
@ -266,6 +365,7 @@ impl cosmic::Application for App {
|
||||||
.progress(1.0)
|
.progress(1.0)
|
||||||
.width(Length::Fill),
|
.width(Length::Fill),
|
||||||
)
|
)
|
||||||
|
.push(widget::button::suggested("asdf").on_press(Message::Ignore))
|
||||||
.spacing(cosmic::theme::spacing().space_s)
|
.spacing(cosmic::theme::spacing().space_s)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.height(Length::Shrink)
|
.height(Length::Shrink)
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ edition = "2024"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
jiff = "0.2"
|
jiff = "0.2"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
tracing-log = "0.2.0"
|
tracing-log = "0.2.0"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = [
|
features = [
|
||||||
"debug",
|
"debug",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ publish = false
|
||||||
[dependencies]
|
[dependencies]
|
||||||
apply = "0.3.0"
|
apply = "0.3.0"
|
||||||
fraction = "0.15.3"
|
fraction = "0.15.3"
|
||||||
libcosmic = { path = "../..", features = [
|
libcosmic-yoda = { path = "../..", features = [
|
||||||
"debug",
|
"debug",
|
||||||
"winit",
|
"winit",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,6 @@ edition = "2021"
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = ["debug", "winit", "wgpu", "tokio"]
|
features = ["debug", "winit", "wgpu", "tokio"]
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
tracing-log = "0.2.0"
|
tracing-log = "0.2.0"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
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
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[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-subscriber = "0.3.22"
|
||||||
tracing-log = "0.2.0"
|
tracing-log = "0.2.0"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
features = ["debug", "winit", "tokio", "xdg-portal", "wgpu"]
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use cosmic::app::{Core, Settings, Task};
|
use cosmic::app::{Core, Settings, Task};
|
||||||
use cosmic::iced::Size;
|
use cosmic::iced::Size;
|
||||||
use cosmic::widget::{menu, nav_bar};
|
use cosmic::widget::{menu, nav_bar, space};
|
||||||
use cosmic::{executor, iced, ApplicationExt, Element};
|
use cosmic::{executor, iced, ApplicationExt, Element};
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
#[derive(Clone, Copy)]
|
||||||
|
|
@ -127,18 +127,20 @@ impl cosmic::Application for App {
|
||||||
Some(&self.nav_model)
|
Some(&self.nav_model)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context menu to display for the given nav bar item ID.
|
/// The context menu list.
|
||||||
fn nav_context_menu(
|
fn nav_context_menu(&self) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
||||||
&self,
|
Some(menu::nav_context(
|
||||||
id: nav_bar::Id,
|
|
||||||
) -> Option<Vec<menu::Tree<cosmic::Action<Self::Message>>>> {
|
|
||||||
Some(menu::items(
|
|
||||||
&HashMap::new(),
|
&HashMap::new(),
|
||||||
|
self.nav_model
|
||||||
|
.iter()
|
||||||
|
.map(|e| {
|
||||||
vec![
|
vec![
|
||||||
menu::Item::Button("Move Up", None, NavMenuAction::MoveUp(id)),
|
menu::Item::Button("Move Up", None, NavMenuAction::MoveUp(e)),
|
||||||
menu::Item::Button("Move Down", None, NavMenuAction::MoveDown(id)),
|
menu::Item::Button("Move Down", None, NavMenuAction::MoveDown(e)),
|
||||||
menu::Item::Button("Delete", None, NavMenuAction::Delete(id)),
|
menu::Item::Button("Delete", None, NavMenuAction::Delete(e)),
|
||||||
],
|
]
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,8 @@ edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["xdg-portal"]
|
default = ["xdg-portal"]
|
||||||
rfd = ["libcosmic/rfd"]
|
rfd = ["libcosmic-yoda/rfd"]
|
||||||
xdg-portal = ["libcosmic/xdg-portal"]
|
xdg-portal = ["libcosmic-yoda/xdg-portal"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
apply = "0.3.0"
|
apply = "0.3.0"
|
||||||
|
|
@ -15,6 +15,6 @@ tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
url = "2.5.8"
|
url = "2.5.8"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
features = ["debug", "winit", "wgpu", "wayland", "tokio"]
|
features = ["debug", "winit", "wgpu", "wayland", "tokio"]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ edition = "2021"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
fraction = "0.15.3"
|
fraction = "0.15.3"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
||||||
path = "../.."
|
path = "../.."
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,6 @@ edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = ["debug", "winit", "wgpu", "tokio", "xdg-portal"]
|
features = ["debug", "winit", "wgpu", "tokio", "xdg-portal"]
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@ tracing-subscriber = "0.3.22"
|
||||||
tracing-log = "0.2.0"
|
tracing-log = "0.2.0"
|
||||||
chrono = "*"
|
chrono = "*"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
features = ["debug", "wgpu", "winit", "desktop", "tokio"]
|
||||||
path = "../.."
|
path = "../.."
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@ tracing = "0.1.44"
|
||||||
tracing-subscriber = "0.3.22"
|
tracing-subscriber = "0.3.22"
|
||||||
tracing-log = "0.2.0"
|
tracing-log = "0.2.0"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic-yoda]
|
||||||
path = "../../"
|
path = "../../"
|
||||||
features = ["debug", "winit", "wgpu", "tokio", "xdg-portal"]
|
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