commit
This commit is contained in:
parent
9dc981b909
commit
78291b75b7
15 changed files with 258 additions and 380 deletions
244
Cargo.lock
generated
244
Cargo.lock
generated
|
@ -128,12 +128,6 @@ version = "0.21.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
checksum = "35636a1494ede3b646cc98f74f8e62c773a38a659ebc777a2cf26b9b74171df9"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit_field"
|
|
||||||
version = "0.10.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.3.2"
|
version = "1.3.2"
|
||||||
|
@ -158,12 +152,6 @@ version = "1.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
|
checksum = "374d28ec25809ee0e23827c2ab573d729e293f281dfe393500e7ad618baa61c6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "byteorder"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytes"
|
name = "bytes"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
|
@ -229,7 +217,7 @@ dependencies = [
|
||||||
"heck",
|
"heck",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -277,6 +265,12 @@ version = "1.0.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation-sys"
|
name = "core-foundation-sys"
|
||||||
version = "0.8.6"
|
version = "0.8.6"
|
||||||
|
@ -313,43 +307,6 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-deque"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fca89a0e215bab21874660c67903c5f143333cab1da83d041c7ded6053774751"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-epoch",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-epoch"
|
|
||||||
version = "0.9.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e3681d554572a651dda4186cd47240627c3d0114d45a95f6ad27f2f22e7548d"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c3a430a770ebd84726f584a90ee7f020d28db52c6d02138900f22341f866d39c"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crunchy"
|
|
||||||
version = "0.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "darling"
|
name = "darling"
|
||||||
version = "0.20.3"
|
version = "0.20.3"
|
||||||
|
@ -371,7 +328,7 @@ dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"strsim",
|
"strsim",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -382,7 +339,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"darling_core",
|
"darling_core",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -395,17 +352,16 @@ checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
|
||||||
name = "deckster"
|
name = "deckster"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytemuck",
|
|
||||||
"bytes",
|
"bytes",
|
||||||
"clap",
|
"clap",
|
||||||
"color-eyre",
|
"color-eyre",
|
||||||
"cosmic-text",
|
"cosmic-text",
|
||||||
|
"derive_more",
|
||||||
"enum-map",
|
"enum-map",
|
||||||
"enum-ordinalize",
|
"enum-ordinalize",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"flume",
|
"flume",
|
||||||
"humantime-serde",
|
"humantime-serde",
|
||||||
"image",
|
|
||||||
"log",
|
"log",
|
||||||
"loupedeck_serial",
|
"loupedeck_serial",
|
||||||
"regex",
|
"regex",
|
||||||
|
@ -432,10 +388,17 @@ dependencies = [
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "derive_more"
|
||||||
version = "1.9.0"
|
version = "0.99.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
|
checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"rustc_version",
|
||||||
|
"syn 1.0.109",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "enum-map"
|
name = "enum-map"
|
||||||
|
@ -454,7 +417,7 @@ checksum = "44600091ce205df4f8b661e98617d49c37b2dd609e449ec82b0fb5d7b33e2eeb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -474,7 +437,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -495,7 +458,7 @@ dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -527,22 +490,6 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "exr"
|
|
||||||
version = "1.71.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
|
|
||||||
dependencies = [
|
|
||||||
"bit_field",
|
|
||||||
"flume",
|
|
||||||
"half",
|
|
||||||
"lebe",
|
|
||||||
"miniz_oxide",
|
|
||||||
"rayon-core",
|
|
||||||
"smallvec",
|
|
||||||
"zune-inflate",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eyre"
|
name = "eyre"
|
||||||
version = "0.6.11"
|
version = "0.6.11"
|
||||||
|
@ -674,15 +621,6 @@ version = "0.28.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "half"
|
|
||||||
version = "2.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
|
|
||||||
dependencies = [
|
|
||||||
"crunchy",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.12.3"
|
version = "0.12.3"
|
||||||
|
@ -758,25 +696,6 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "image"
|
|
||||||
version = "0.24.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
"byteorder",
|
|
||||||
"color_quant",
|
|
||||||
"exr",
|
|
||||||
"gif",
|
|
||||||
"jpeg-decoder",
|
|
||||||
"num-rational",
|
|
||||||
"num-traits",
|
|
||||||
"png",
|
|
||||||
"qoi",
|
|
||||||
"tiff",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imagesize"
|
name = "imagesize"
|
||||||
version = "0.12.0"
|
version = "0.12.0"
|
||||||
|
@ -843,9 +762,6 @@ name = "jpeg-decoder"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||||
dependencies = [
|
|
||||||
"rayon",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "js-sys"
|
name = "js-sys"
|
||||||
|
@ -871,12 +787,6 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lebe"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.151"
|
version = "0.2.151"
|
||||||
|
@ -1007,27 +917,6 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-integer"
|
|
||||||
version = "0.1.45"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "num-rational"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"num-integer",
|
|
||||||
"num-traits",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
|
@ -1137,15 +1026,6 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "qoi"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
|
||||||
dependencies = [
|
|
||||||
"bytemuck",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.33"
|
version = "1.0.33"
|
||||||
|
@ -1161,26 +1041,6 @@ version = "1.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "977b1e897f9d764566891689e642653e5ed90c6895106acd005eb4c1d0203991"
|
checksum = "977b1e897f9d764566891689e642653e5ed90c6895106acd005eb4c1d0203991"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon"
|
|
||||||
version = "1.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
"rayon-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rayon-core"
|
|
||||||
version = "1.12.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
|
|
||||||
dependencies = [
|
|
||||||
"crossbeam-deque",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rctree"
|
name = "rctree"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
|
@ -1278,6 +1138,15 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.28"
|
version = "0.38.28"
|
||||||
|
@ -1351,6 +1220,12 @@ version = "1.0.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
|
checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.193"
|
version = "1.0.193"
|
||||||
|
@ -1368,7 +1243,7 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1427,7 +1302,7 @@ dependencies = [
|
||||||
"darling",
|
"darling",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1538,6 +1413,17 @@ dependencies = [
|
||||||
"zeno",
|
"zeno",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.109"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "2.0.43"
|
version = "2.0.43"
|
||||||
|
@ -1584,7 +1470,7 @@ checksum = "e7fbe9b594d6568a6a1443250a7e67d80b74e1e96f6d1715e1e21cc1888291d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1597,17 +1483,6 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tiff"
|
|
||||||
version = "0.9.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
|
|
||||||
dependencies = [
|
|
||||||
"flate2",
|
|
||||||
"jpeg-decoder",
|
|
||||||
"weezl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "time"
|
name = "time"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
|
@ -1699,7 +1574,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1968,7 +1843,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1990,7 +1865,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn 2.0.43",
|
||||||
"wasm-bindgen-backend",
|
"wasm-bindgen-backend",
|
||||||
"wasm-bindgen-shared",
|
"wasm-bindgen-shared",
|
||||||
]
|
]
|
||||||
|
@ -2202,12 +2077,3 @@ name = "zeno"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
|
checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zune-inflate"
|
|
||||||
version = "0.2.54"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
|
||||||
dependencies = [
|
|
||||||
"simd-adler32",
|
|
||||||
]
|
|
||||||
|
|
|
@ -5,3 +5,6 @@ members = [
|
||||||
]
|
]
|
||||||
|
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
strip = true # Automatically strip symbols from the binary.
|
|
@ -5,16 +5,15 @@ edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bytes = "1.5.0"
|
bytes = "1.5.0"
|
||||||
bytemuck = "1.14.0"
|
|
||||||
clap = { version = "4.4.12", features = ["derive"] }
|
clap = { version = "4.4.12", features = ["derive"] }
|
||||||
color-eyre = "0.6.2"
|
color-eyre = "0.6.2"
|
||||||
cosmic-text = "0.10.0"
|
cosmic-text = "0.10.0"
|
||||||
|
derive_more = "0.99.17"
|
||||||
enum-map = "3.0.0-beta.2"
|
enum-map = "3.0.0-beta.2"
|
||||||
enum-ordinalize = "4.3.0"
|
enum-ordinalize = "4.3.0"
|
||||||
env_logger = "0.10.1"
|
env_logger = "0.10.1"
|
||||||
flume = "0.11.0"
|
flume = "0.11.0"
|
||||||
humantime-serde = "1.1.1"
|
humantime-serde = "1.1.1"
|
||||||
image = "0.24.7"
|
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
loupedeck_serial = { path = "../loupedeck_serial" }
|
loupedeck_serial = { path = "../loupedeck_serial" }
|
||||||
regex = "1.10.2"
|
regex = "1.10.2"
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
key_pages = ["./key-pages/*"]
|
|
||||||
knob_pages = ["./knob-pages/*"]
|
|
||||||
|
|
||||||
inactive_button_color = "#000060"
|
inactive_button_color = "#000060"
|
||||||
active_button_color = "#eeffff"
|
active_button_color = "#eeffff"
|
||||||
|
|
||||||
|
@ -24,16 +21,16 @@ knob_page = "default"
|
||||||
key_page = "default"
|
key_page = "default"
|
||||||
knob_page = "default"
|
knob_page = "default"
|
||||||
|
|
||||||
[[icon_packs]]
|
[icon_packs.apps]
|
||||||
id = "apps"
|
|
||||||
path = "./apps"
|
path = "./apps"
|
||||||
|
format = "svg"
|
||||||
|
|
||||||
[[icon_packs]]
|
[icon_packs.fad]
|
||||||
id = "fad"
|
|
||||||
path = "./fad"
|
path = "./fad"
|
||||||
|
format = "svg"
|
||||||
global_filter = "invert"
|
global_filter = "invert"
|
||||||
|
|
||||||
[[icon_packs]]
|
[icon_packs.ph]
|
||||||
id = "ph"
|
|
||||||
path = "./ph"
|
path = "./ph"
|
||||||
|
format = "svg"
|
||||||
global_filter = "invert"
|
global_filter = "invert"
|
14
deckster/src/icons/filter.rs
Normal file
14
deckster/src/icons/filter.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
use color_eyre::{eyre::ContextCompat, Result};
|
||||||
|
use tiny_skia::Pixmap;
|
||||||
|
|
||||||
|
use crate::model::image_filter::ImageFilter;
|
||||||
|
|
||||||
|
pub fn apply_filter(original: &Pixmap, filter: &ImageFilter) -> Result<Pixmap> {
|
||||||
|
let mut result = if let Some(rect) = filter.crop {
|
||||||
|
original.clone_rect(*rect).wrap_err_with(|| format!("Invalid crop rect: {}", rect))?
|
||||||
|
} else {
|
||||||
|
original.clone()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(result)
|
||||||
|
}
|
|
@ -4,22 +4,42 @@ use std::path::Path;
|
||||||
|
|
||||||
use color_eyre::eyre::{eyre, ContextCompat, WrapErr};
|
use color_eyre::eyre::{eyre, ContextCompat, WrapErr};
|
||||||
use color_eyre::Result;
|
use color_eyre::Result;
|
||||||
use image::RgbaImage;
|
|
||||||
use resvg::usvg::{TextRendering, TreeParsing, TreeTextToPath};
|
use resvg::usvg::{TextRendering, TreeParsing, TreeTextToPath};
|
||||||
use tiny_skia::{Pixmap, PixmapMut, Transform};
|
use tiny_skia::{Pixmap, Transform};
|
||||||
|
|
||||||
use crate::model::config::{IconFormat, IconPack};
|
use crate::icons::filter::apply_filter;
|
||||||
|
use crate::model::config::{Config, IconFormat, IconPack};
|
||||||
use crate::model::icon_descriptor::{IconDescriptor, IconDescriptorSource};
|
use crate::model::icon_descriptor::{IconDescriptor, IconDescriptorSource};
|
||||||
use crate::model::image_filter::ImageFilter;
|
|
||||||
|
mod filter;
|
||||||
|
|
||||||
|
pub struct LoadedIcon {
|
||||||
|
pixmap: Pixmap,
|
||||||
|
scale: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_used_icon_descriptors(config: &Config) -> HashSet<IconDescriptor> {
|
||||||
|
let mut result: HashSet<IconDescriptor> = HashSet::new();
|
||||||
|
|
||||||
|
for page in config.key_pages_by_id.values() {
|
||||||
|
for key in page.keys.values() {
|
||||||
|
if let Some(d) = &key.icon {
|
||||||
|
result.insert(d.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
pub fn load_icons(
|
pub fn load_icons(
|
||||||
icon_packs_by_id: HashMap<String, IconPack>,
|
icon_packs_by_id: HashMap<String, IconPack>,
|
||||||
descriptors: HashSet<IconDescriptor>,
|
descriptors: HashSet<IconDescriptor>,
|
||||||
key_size: (u16, u16),
|
key_size: (u16, u16),
|
||||||
dpi: f32,
|
dpi: f32,
|
||||||
) -> Result<HashMap<IconDescriptor, Pixmap>> {
|
) -> Result<HashMap<IconDescriptor, LoadedIcon>> {
|
||||||
let mut original_images_by_source: HashMap<IconDescriptorSource, RgbaImage> = HashMap::new();
|
let mut unfiltered_pixmap_by_source: HashMap<IconDescriptorSource, Pixmap> = HashMap::new();
|
||||||
let mut icons_by_descriptor: HashMap<IconDescriptor, Pixmap> = HashMap::new();
|
let mut icons_by_descriptor: HashMap<IconDescriptor, LoadedIcon> = HashMap::new();
|
||||||
let mut fonts_db = resvg::usvg::fontdb::Database::new();
|
let mut fonts_db = resvg::usvg::fontdb::Database::new();
|
||||||
fonts_db.load_system_fonts();
|
fonts_db.load_system_fonts();
|
||||||
|
|
||||||
|
@ -28,12 +48,15 @@ pub fn load_icons(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let image = match original_images_by_source.entry(descriptor.source.clone()) {
|
let original_image = match unfiltered_pixmap_by_source.entry(descriptor.source.clone()) {
|
||||||
Entry::Occupied(o) => o.into_mut(),
|
Entry::Occupied(o) => o.into_mut(),
|
||||||
Entry::Vacant(v) => v.insert(read_image(&icon_packs_by_id, dpi, &fonts_db, descriptor.source)?),
|
Entry::Vacant(v) => v.insert(read_image(&icon_packs_by_id, dpi, &fonts_db, descriptor.source.clone())?),
|
||||||
};
|
};
|
||||||
|
|
||||||
dbg!(image.height());
|
let pixmap = apply_filter(original_image, &descriptor.filter)?;
|
||||||
|
let scale = descriptor.filter.scale;
|
||||||
|
|
||||||
|
icons_by_descriptor.insert(descriptor, LoadedIcon { pixmap, scale });
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(icons_by_descriptor)
|
Ok(icons_by_descriptor)
|
||||||
|
@ -44,16 +67,16 @@ fn read_image(
|
||||||
dpi: f32,
|
dpi: f32,
|
||||||
fonts_db: &resvg::usvg::fontdb::Database,
|
fonts_db: &resvg::usvg::fontdb::Database,
|
||||||
source: IconDescriptorSource,
|
source: IconDescriptorSource,
|
||||||
) -> Result<RgbaImage> {
|
) -> Result<Pixmap> {
|
||||||
let path = match source {
|
let path = match source {
|
||||||
IconDescriptorSource::None => unreachable!(),
|
IconDescriptorSource::None => return Ok(Pixmap::new(1, 1).unwrap()),
|
||||||
IconDescriptorSource::Path(path) => path,
|
IconDescriptorSource::Path(path) => path,
|
||||||
IconDescriptorSource::IconPack { pack_id, icon_id } => {
|
IconDescriptorSource::IconPack { pack_id, icon_id } => {
|
||||||
let pack = icon_packs_by_id.get(&pack_id).wrap_err_with(|| format!("Unknown icon pack: @{}", pack_id))?;
|
let pack = icon_packs_by_id.get(&pack_id).wrap_err_with(|| format!("Unknown icon pack: @{}", pack_id))?;
|
||||||
|
|
||||||
let extension = match pack.format {
|
let extension = match pack.format {
|
||||||
IconFormat::PNG => "png",
|
IconFormat::Png => "png",
|
||||||
IconFormat::SVG => "svg",
|
IconFormat::Svg => "svg",
|
||||||
};
|
};
|
||||||
|
|
||||||
pack.path.join(icon_id + "." + extension)
|
pack.path.join(icon_id + "." + extension)
|
||||||
|
@ -63,9 +86,7 @@ fn read_image(
|
||||||
Ok(match path.extension() {
|
Ok(match path.extension() {
|
||||||
None => return Err(eyre!("Invalid icon path: {:?}", path)),
|
None => return Err(eyre!("Invalid icon path: {:?}", path)),
|
||||||
Some(extension) => match extension.to_string_lossy().as_ref() {
|
Some(extension) => match extension.to_string_lossy().as_ref() {
|
||||||
"png" => image::open(path.clone())
|
"png" => Pixmap::load_png(&path).wrap_err_with(|| format!("Failed to open or decode the PNG file at {}", path.to_string_lossy()))?,
|
||||||
.wrap_err_with(|| format!("Failed to open or decode the PNG file at {}", path.to_string_lossy()))?
|
|
||||||
.into_rgba8(),
|
|
||||||
"svg" => {
|
"svg" => {
|
||||||
read_image_from_svg(&path, dpi, fonts_db).wrap_err_with(|| format!("Failed to open or decode the SVG file at {}", path.to_string_lossy()))?
|
read_image_from_svg(&path, dpi, fonts_db).wrap_err_with(|| format!("Failed to open or decode the SVG file at {}", path.to_string_lossy()))?
|
||||||
}
|
}
|
||||||
|
@ -74,52 +95,30 @@ fn read_image(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_image_from_svg(path: &Path, dpi: f32, font_db: &resvg::usvg::fontdb::Database) -> Result<RgbaImage> {
|
fn read_image_from_svg(path: &Path, dpi: f32, font_db: &resvg::usvg::fontdb::Database) -> Result<Pixmap> {
|
||||||
let data = std::fs::read(path)?;
|
let raw_data = std::fs::read(path)?;
|
||||||
let mut tree = resvg::usvg::Tree::from_data(
|
|
||||||
&data,
|
|
||||||
&resvg::usvg::Options {
|
|
||||||
dpi,
|
|
||||||
font_family: "Inter".to_owned(),
|
|
||||||
font_size: 11.0,
|
|
||||||
text_rendering: TextRendering::OptimizeLegibility,
|
|
||||||
..resvg::usvg::Options::default()
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
tree.convert_text(font_db);
|
let tree = {
|
||||||
|
let mut tree = resvg::usvg::Tree::from_data(
|
||||||
|
&raw_data,
|
||||||
|
&resvg::usvg::Options {
|
||||||
|
dpi,
|
||||||
|
font_family: "Inter".to_owned(),
|
||||||
|
font_size: 11.0,
|
||||||
|
text_rendering: TextRendering::OptimizeLegibility,
|
||||||
|
..resvg::usvg::Options::default()
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
let render_tree = resvg::Tree::from_usvg(&tree);
|
tree.convert_text(font_db);
|
||||||
let width = render_tree.size.width().ceil() as u32;
|
|
||||||
let height = render_tree.size.height().ceil() as u32;
|
|
||||||
let mut pixmap = Pixmap::new(width, height).unwrap();
|
|
||||||
let mut pixmap_mut: PixmapMut = pixmap.as_mut();
|
|
||||||
|
|
||||||
render_tree.render(Transform::identity(), &mut pixmap_mut);
|
resvg::Tree::from_usvg(&tree)
|
||||||
|
};
|
||||||
|
|
||||||
Ok(RgbaImage::from_vec(
|
let size = tree.size.to_int_size();
|
||||||
width,
|
let mut pixmap = Pixmap::new(size.width(), size.height()).unwrap();
|
||||||
height,
|
|
||||||
pixmap
|
tree.render(Transform::default(), &mut pixmap.as_mut());
|
||||||
.pixels_mut()
|
|
||||||
.iter()
|
Ok(pixmap)
|
||||||
.flat_map(|p| {
|
|
||||||
let p = p.demultiply();
|
|
||||||
[p.red(), p.green(), p.blue(), p.alpha()]
|
|
||||||
})
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
.unwrap())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_filter(original: &RgbaImage, filter: &ImageFilter) -> RgbaImage {
|
|
||||||
let mut result = original.clone();
|
|
||||||
|
|
||||||
if filter.alpha != 1.0 {
|
|
||||||
for pixel in result.pixels_mut() {
|
|
||||||
pixel.0[3] = (pixel.0[3] as f32 * filter.alpha).floor() as u8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,7 @@ mod runner;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(name = "deckster")]
|
#[command(name = "deckster")]
|
||||||
#[command(about = "Use Loupedeck device under Linux.")]
|
#[command(about = "Use Loupedeck devices under Linux.")]
|
||||||
struct Cli {
|
struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
command: Command,
|
command: Command,
|
||||||
|
@ -36,11 +36,11 @@ pub async fn main() -> Result<()> {
|
||||||
|
|
||||||
match cli.command {
|
match cli.command {
|
||||||
Command::Run { config: config_path } => {
|
Command::Run { config: config_path } => {
|
||||||
let deckster_file = read_and_deserialize::<model::config::File>(config_path.join("./deckster.toml").as_path())?;
|
let deckster_file = read_and_deserialize::<model::config::File>(config_path.join("deckster.toml").as_path())?;
|
||||||
let config_path = config_path.canonicalize()?;
|
let config_path = config_path.canonicalize()?;
|
||||||
|
|
||||||
let key_pages_by_id: HashMap<String, model::key_page::Page> =
|
let key_pages_by_id: HashMap<String, model::key_page::Page> =
|
||||||
read_and_deserialize_from_directory::<model::key_page::File>(config_path.join("./key-pages").as_path())?
|
read_and_deserialize_from_directory::<model::key_page::File>(config_path.join("key-pages").as_path())?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| model::key_page::Page {
|
.map(|p| model::key_page::Page {
|
||||||
id: p.inner.id.clone().unwrap_or(p.fallback_id),
|
id: p.inner.id.clone().unwrap_or(p.fallback_id),
|
||||||
|
@ -51,7 +51,7 @@ pub async fn main() -> Result<()> {
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let knob_pages_by_id: HashMap<String, model::knob_page::Page> =
|
let knob_pages_by_id: HashMap<String, model::knob_page::Page> =
|
||||||
read_and_deserialize_from_directory::<model::knob_page::File>(config_path.join("./knob-pages").as_path())?
|
read_and_deserialize_from_directory::<model::knob_page::File>(config_path.join("knob-pages").as_path())?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|p| model::knob_page::Page {
|
.map(|p| model::knob_page::Page {
|
||||||
id: p.inner.id.clone().unwrap_or(p.fallback_id),
|
id: p.inner.id.clone().unwrap_or(p.fallback_id),
|
||||||
|
@ -79,7 +79,7 @@ pub async fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_and_deserialize<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T> {
|
fn read_and_deserialize<T: serde::de::DeserializeOwned>(path: &Path) -> Result<T> {
|
||||||
let content = fs::read_to_string(path).wrap_err_with(|| format!("Reading of {} failed.", path.to_string_lossy()))?;
|
let content = fs::read_to_string(&path).wrap_err_with(|| format!("Reading of {} failed.", path.to_string_lossy()))?;
|
||||||
|
|
||||||
toml::from_str::<T>(&content).wrap_err_with(|| format!("Parsing of {} failed.", path.to_string_lossy()))
|
toml::from_str::<T>(&content).wrap_err_with(|| format!("Parsing of {} failed.", path.to_string_lossy()))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,16 +8,16 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::model;
|
use crate::model;
|
||||||
use crate::model::image_filter::ImageFilter;
|
use crate::model::image_filter::ImageFilter;
|
||||||
use crate::model::rgb::SerializableRGB8;
|
use crate::model::rgb::RGB8Wrapper;
|
||||||
use crate::model::ButtonPosition;
|
use crate::model::ButtonPosition;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
pub icon_packs: HashMap<String, IconPack>,
|
pub icon_packs: HashMap<String, IconPack>,
|
||||||
#[serde(default = "inactive_button_color_default")]
|
#[serde(default = "inactive_button_color_default")]
|
||||||
pub inactive_button_color: SerializableRGB8,
|
pub inactive_button_color: RGB8Wrapper,
|
||||||
#[serde(default = "active_button_color_default")]
|
#[serde(default = "active_button_color_default")]
|
||||||
pub active_button_color: SerializableRGB8,
|
pub active_button_color: RGB8Wrapper,
|
||||||
pub buttons: HashMap<ButtonPosition, ButtonConfig>, // EnumMap
|
pub buttons: HashMap<ButtonPosition, ButtonConfig>, // EnumMap
|
||||||
pub initial: InitialConfig,
|
pub initial: InitialConfig,
|
||||||
}
|
}
|
||||||
|
@ -33,18 +33,18 @@ pub struct Config {
|
||||||
pub key_pages_by_id: HashMap<String, model::key_page::Page>,
|
pub key_pages_by_id: HashMap<String, model::key_page::Page>,
|
||||||
pub knob_pages_by_id: HashMap<String, model::knob_page::Page>,
|
pub knob_pages_by_id: HashMap<String, model::knob_page::Page>,
|
||||||
pub icon_packs: HashMap<String, IconPack>,
|
pub icon_packs: HashMap<String, IconPack>,
|
||||||
pub inactive_button_color: SerializableRGB8,
|
pub inactive_button_color: RGB8Wrapper,
|
||||||
pub active_button_color: SerializableRGB8,
|
pub active_button_color: RGB8Wrapper,
|
||||||
pub buttons: EnumMap<ButtonPosition, ButtonConfig>,
|
pub buttons: EnumMap<ButtonPosition, ButtonConfig>,
|
||||||
pub initial: InitialConfig,
|
pub initial: InitialConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inactive_button_color_default() -> SerializableRGB8 {
|
fn inactive_button_color_default() -> RGB8Wrapper {
|
||||||
SerializableRGB8(RGB8::new(128, 128, 128))
|
RGB8::new(128, 128, 128).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn active_button_color_default() -> SerializableRGB8 {
|
fn active_button_color_default() -> RGB8Wrapper {
|
||||||
SerializableRGB8(RGB8::new(0, 255, 0))
|
RGB8::new(0, 255, 0).into()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Default)]
|
#[derive(Debug, Deserialize, Default)]
|
||||||
|
@ -62,13 +62,12 @@ pub struct InitialConfig {
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum IconFormat {
|
pub enum IconFormat {
|
||||||
PNG,
|
Png,
|
||||||
SVG,
|
Svg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct IconPack {
|
pub struct IconPack {
|
||||||
pub id: String,
|
|
||||||
pub path: PathBuf,
|
pub path: PathBuf,
|
||||||
pub format: IconFormat,
|
pub format: IconFormat,
|
||||||
pub global_filter: Option<ImageFilter>,
|
pub global_filter: Option<ImageFilter>,
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use derive_more::{Deref, From, Into};
|
||||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tiny_skia::IntRect;
|
use tiny_skia::IntRect;
|
||||||
|
@ -39,25 +40,39 @@ impl Display for UIntVec2 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_positive_int_rect_from_str(s: &str) -> Result<IntRect, ()> {
|
#[derive(Debug, Error)]
|
||||||
let (pairs, is_corner_points_mode) = if let Some(pairs) = s.split_once('+') {
|
#[error("The input value does not match the required format.")]
|
||||||
(pairs, false)
|
pub struct ParsingError {}
|
||||||
} else if let Some(pairs) = s.split_once('-') {
|
|
||||||
(pairs, true)
|
|
||||||
} else {
|
|
||||||
return Err(());
|
|
||||||
};
|
|
||||||
|
|
||||||
let first_vec = UIntVec2::from_str(pairs.0).map_err(|_| ())?;
|
#[derive(Debug, Copy, Clone, PartialEq, Deref, From, Into, SerializeDisplay, DeserializeFromStr)]
|
||||||
let second_vec = UIntVec2::from_str(pairs.1).map_err(|_| ())?;
|
#[repr(transparent)]
|
||||||
|
pub struct IntRectWrapper(IntRect);
|
||||||
|
|
||||||
Ok(if is_corner_points_mode {
|
impl FromStr for IntRectWrapper {
|
||||||
IntRect::from_ltrb(first_vec.x as i32, first_vec.y as i32, second_vec.x as i32, second_vec.y as i32).unwrap()
|
type Err = ParsingError;
|
||||||
} else {
|
|
||||||
IntRect::from_xywh(first_vec.x as i32, first_vec.y as i32, second_vec.x, second_vec.y).unwrap()
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
})
|
let (pairs, is_corner_points_mode) = if let Some(pairs) = s.split_once('+') {
|
||||||
|
(pairs, false)
|
||||||
|
} else if let Some(pairs) = s.split_once('-') {
|
||||||
|
(pairs, true)
|
||||||
|
} else {
|
||||||
|
return Err(ParsingError {});
|
||||||
|
};
|
||||||
|
|
||||||
|
let first_vec = UIntVec2::from_str(pairs.0).map_err(|_| ParsingError {})?;
|
||||||
|
let second_vec = UIntVec2::from_str(pairs.1).map_err(|_| ParsingError {})?;
|
||||||
|
|
||||||
|
Ok(IntRectWrapper(if is_corner_points_mode {
|
||||||
|
IntRect::from_ltrb(first_vec.x as i32, first_vec.y as i32, second_vec.x as i32, second_vec.y as i32).unwrap()
|
||||||
|
} else {
|
||||||
|
IntRect::from_xywh(first_vec.x as i32, first_vec.y as i32, second_vec.x, second_vec.y).unwrap()
|
||||||
|
}))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_positive_rect(rect: &IntRect, f: &mut Formatter<'_>) -> std::fmt::Result {
|
impl Display for IntRectWrapper {
|
||||||
f.write_fmt(format_args!("{}x{}+{}x{}", rect.x(), rect.y(), rect.width(), rect.height()))
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_fmt(format_args!("{}x{}+{}x{}", self.x(), self.y(), self.width(), self.height()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ pub struct IconDescriptorString(pub IconDescriptor);
|
||||||
#[derive(Debug, Default, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Default, Eq, PartialEq, Hash, Clone, Serialize, Deserialize)]
|
||||||
pub struct IconDescriptor {
|
pub struct IconDescriptor {
|
||||||
pub source: IconDescriptorSource,
|
pub source: IconDescriptorSource,
|
||||||
pub filter: Option<ImageFilter>,
|
pub filter: ImageFilter,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
|
@ -46,15 +46,15 @@ impl FromStr for IconDescriptorString {
|
||||||
IconDescriptorSource::Path(PathBuf::from(raw_source))
|
IconDescriptorSource::Path(PathBuf::from(raw_source))
|
||||||
};
|
};
|
||||||
|
|
||||||
let filter: Option<ImageFilter> = if raw_filter.is_empty() {
|
let filter: ImageFilter = if raw_filter.is_empty() {
|
||||||
None
|
ImageFilter::default()
|
||||||
} else {
|
} else {
|
||||||
let mut raw_filter = raw_filter.to_owned();
|
let mut raw_filter = raw_filter.to_owned();
|
||||||
if raw_filter.pop().expect("emptiness was eliminated a few lines earlier") != ']' {
|
if raw_filter.pop().expect("emptiness was eliminated a few lines earlier") != ']' {
|
||||||
return Err(IconDescriptorFromStrError::MissingImageFilterClosingBracket);
|
return Err(IconDescriptorFromStrError::MissingImageFilterClosingBracket);
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(ImageFilter::from_str(&raw_filter).map_err(IconDescriptorFromStrError::InvalidImageFilter)?)
|
ImageFilter::from_str(&raw_filter).map_err(IconDescriptorFromStrError::InvalidImageFilter)?
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(IconDescriptorString(IconDescriptor { source, filter }))
|
Ok(IconDescriptorString(IconDescriptor { source, filter }))
|
||||||
|
|
|
@ -1,22 +1,19 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Debug, Display, Formatter};
|
||||||
use std::hash::{Hash, Hasher};
|
use std::hash::{Hash, Hasher};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use rgb::RGB8;
|
|
||||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tiny_skia::IntRect;
|
|
||||||
|
|
||||||
use crate::model::geometry::{fmt_positive_rect, parse_positive_int_rect_from_str};
|
use crate::model::geometry::IntRectWrapper;
|
||||||
use crate::model::rgb::{fmt_rgb8_as_hex_string, parse_rgb8_from_hex_str};
|
use crate::model::rgb::RGB8Wrapper;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, SerializeDisplay, DeserializeFromStr)]
|
#[derive(Debug, PartialEq, Clone, SerializeDisplay, DeserializeFromStr)]
|
||||||
pub struct ImageFilter {
|
pub struct ImageFilter {
|
||||||
pub crop_original: Option<IntRect>, // applied before scale and rotate
|
pub crop: Option<IntRectWrapper>,
|
||||||
pub scale: f32,
|
pub scale: f32,
|
||||||
pub clockwise_quarter_rotations: u8,
|
pub clockwise_quarter_rotations: u8,
|
||||||
pub crop: Option<IntRect>, // applied after scale and rotate
|
pub color: Option<RGB8Wrapper>,
|
||||||
pub color: Option<RGB8>,
|
|
||||||
pub alpha: f32,
|
pub alpha: f32,
|
||||||
pub blur: f32,
|
pub blur: f32,
|
||||||
pub grayscale: bool,
|
pub grayscale: bool,
|
||||||
|
@ -34,7 +31,6 @@ impl Hash for ImageFilter {
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_IMAGE_FILTER: ImageFilter = ImageFilter {
|
const DEFAULT_IMAGE_FILTER: ImageFilter = ImageFilter {
|
||||||
crop_original: None,
|
|
||||||
scale: 1.0,
|
scale: 1.0,
|
||||||
clockwise_quarter_rotations: 0,
|
clockwise_quarter_rotations: 0,
|
||||||
crop: None,
|
crop: None,
|
||||||
|
@ -79,8 +75,8 @@ impl FromStr for ImageFilter {
|
||||||
type Err = ImageFilterFromStringError;
|
type Err = ImageFilterFromStringError;
|
||||||
|
|
||||||
fn from_str<'a>(s: &str) -> Result<Self, Self::Err> {
|
fn from_str<'a>(s: &str) -> Result<Self, Self::Err> {
|
||||||
fn parse_rect_filter_value(filter_name: &str, raw_value: String) -> Result<IntRect, ImageFilterFromStringError> {
|
fn parse_rect_filter_value(filter_name: &str, raw_value: String) -> Result<IntRectWrapper, ImageFilterFromStringError> {
|
||||||
parse_positive_int_rect_from_str(&raw_value).map_err(|_| ImageFilterFromStringError::FilterValueNotParsable {
|
IntRectWrapper::from_str(&raw_value).map_err(|_| ImageFilterFromStringError::FilterValueNotParsable {
|
||||||
filter_name: filter_name.to_string(),
|
filter_name: filter_name.to_string(),
|
||||||
raw_value,
|
raw_value,
|
||||||
})
|
})
|
||||||
|
@ -145,7 +141,7 @@ impl FromStr for ImageFilter {
|
||||||
};
|
};
|
||||||
|
|
||||||
match filter_name.as_str() {
|
match filter_name.as_str() {
|
||||||
"crop_original" => result.crop_original = Some(parse_rect_filter_value(&filter_name, use_raw_value()?)?),
|
"crop" => result.crop = Some(parse_rect_filter_value(&filter_name, use_raw_value()?)?),
|
||||||
"scale" => result.scale = parse_f32_filter_value(&filter_name, use_raw_value()?, "0..=100", Box::new(|v| (0.0..=100.0).contains(v)))?,
|
"scale" => result.scale = parse_f32_filter_value(&filter_name, use_raw_value()?, "0..=100", Box::new(|v| (0.0..=100.0).contains(v)))?,
|
||||||
"rotate" => {
|
"rotate" => {
|
||||||
let raw_value = use_raw_value()?;
|
let raw_value = use_raw_value()?;
|
||||||
|
@ -162,12 +158,11 @@ impl FromStr for ImageFilter {
|
||||||
_ => return Err(ImageFilterFromStringError::RotationNotAllowed),
|
_ => return Err(ImageFilterFromStringError::RotationNotAllowed),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
"crop" => result.crop = Some(parse_rect_filter_value(&filter_name, use_raw_value()?)?),
|
|
||||||
"color" => {
|
"color" => {
|
||||||
let raw_value = use_raw_value()?;
|
let raw_value = use_raw_value()?;
|
||||||
|
|
||||||
result.color = Some(
|
result.color = Some(
|
||||||
parse_rgb8_from_hex_str(&raw_value).map_err(|_| ImageFilterFromStringError::FilterValueNotParsable {
|
RGB8Wrapper::from_str(&raw_value).map_err(|_| ImageFilterFromStringError::FilterValueNotParsable {
|
||||||
filter_name: filter_name.to_string(),
|
filter_name: filter_name.to_string(),
|
||||||
raw_value,
|
raw_value,
|
||||||
})?,
|
})?,
|
||||||
|
@ -188,12 +183,12 @@ impl FromStr for ImageFilter {
|
||||||
impl Display for ImageFilter {
|
impl Display for ImageFilter {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
let mut is_first = true;
|
let mut is_first = true;
|
||||||
if let Some(rect) = self.crop_original {
|
|
||||||
if !is_first {
|
if let Some(rect) = &self.crop {
|
||||||
f.write_str("|")?
|
// if !is_first {
|
||||||
}
|
// f.write_str("|")?
|
||||||
f.write_str("crop_original=")?;
|
// }
|
||||||
fmt_positive_rect(&rect, f)?;
|
f.write_fmt(format_args!("crop={}", rect))?;
|
||||||
is_first = false;
|
is_first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,8 +196,7 @@ impl Display for ImageFilter {
|
||||||
if !is_first {
|
if !is_first {
|
||||||
f.write_str("|")?
|
f.write_str("|")?
|
||||||
}
|
}
|
||||||
f.write_str("scale=")?;
|
f.write_fmt(format_args!("scale={}", self.scale))?;
|
||||||
self.scale.fmt(f)?;
|
|
||||||
is_first = false;
|
is_first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,17 +204,8 @@ impl Display for ImageFilter {
|
||||||
if !is_first {
|
if !is_first {
|
||||||
f.write_str("|")?
|
f.write_str("|")?
|
||||||
}
|
}
|
||||||
f.write_str("rotate=")?;
|
|
||||||
(self.clockwise_quarter_rotations * 90).fmt(f)?;
|
|
||||||
is_first = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(rect) = self.crop {
|
f.write_fmt(format_args!("rotate={}", self.clockwise_quarter_rotations * 90))?;
|
||||||
if !is_first {
|
|
||||||
f.write_str("|")?
|
|
||||||
}
|
|
||||||
f.write_str("crop=")?;
|
|
||||||
fmt_positive_rect(&rect, f)?;
|
|
||||||
is_first = false;
|
is_first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -228,8 +213,8 @@ impl Display for ImageFilter {
|
||||||
if !is_first {
|
if !is_first {
|
||||||
f.write_str("|")?
|
f.write_str("|")?
|
||||||
}
|
}
|
||||||
f.write_str("color=")?;
|
|
||||||
fmt_rgb8_as_hex_string(&color, f)?;
|
f.write_fmt(format_args!("color={}", color))?;
|
||||||
is_first = false;
|
is_first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,8 +222,7 @@ impl Display for ImageFilter {
|
||||||
if !is_first {
|
if !is_first {
|
||||||
f.write_str("|")?
|
f.write_str("|")?
|
||||||
}
|
}
|
||||||
f.write_str("alpha=")?;
|
f.write_fmt(format_args!("alpha={}", self.alpha))?;
|
||||||
self.alpha.fmt(f)?;
|
|
||||||
is_first = false;
|
is_first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -246,8 +230,7 @@ impl Display for ImageFilter {
|
||||||
if !is_first {
|
if !is_first {
|
||||||
f.write_str("|")?
|
f.write_str("|")?
|
||||||
}
|
}
|
||||||
f.write_str("blur=")?;
|
f.write_fmt(format_args!("blur={}", self.blur))?;
|
||||||
self.blur.fmt(f)?;
|
|
||||||
is_first = false;
|
is_first = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use regex::Regex;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::model::icon_descriptor::IconDescriptorString;
|
use crate::model::icon_descriptor::IconDescriptorString;
|
||||||
use crate::model::rgb::SerializableRGB8WithOptionalAlpha;
|
use crate::model::rgb::RGB8WithOptionalA;
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
|
@ -54,12 +54,12 @@ pub enum State {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct CircleIndicatorConfig {
|
pub struct CircleIndicatorConfig {
|
||||||
pub color: SerializableRGB8WithOptionalAlpha,
|
pub color: RGB8WithOptionalA,
|
||||||
pub width: NonZeroU8,
|
pub width: NonZeroU8,
|
||||||
pub radius: u8,
|
pub radius: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct BarIndicatorConfig {
|
pub struct BarIndicatorConfig {
|
||||||
pub color: SerializableRGB8WithOptionalAlpha,
|
pub color: RGB8WithOptionalA,
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ use enum_map::EnumMap;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::model::icon_descriptor::IconDescriptorString;
|
use crate::model::icon_descriptor::IconDescriptorString;
|
||||||
use crate::model::rgb::SerializableRGB8WithOptionalAlpha;
|
use crate::model::rgb::RGB8WithOptionalA;
|
||||||
use crate::model::{knob_modes, KnobPosition};
|
use crate::model::{knob_modes, KnobPosition};
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
@ -39,12 +39,12 @@ pub struct KnobIndicators {
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct KnobIndicatorBarConfig {
|
pub struct KnobIndicatorBarConfig {
|
||||||
pub color: SerializableRGB8WithOptionalAlpha,
|
pub color: RGB8WithOptionalA,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct KnobIndicatorCircleConfig {
|
pub struct KnobIndicatorCircleConfig {
|
||||||
pub color: SerializableRGB8WithOptionalAlpha,
|
pub color: RGB8WithOptionalA,
|
||||||
pub width: u8,
|
pub width: u8,
|
||||||
pub radius: u8,
|
pub radius: u8,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,104 +1,107 @@
|
||||||
use std::fmt::{Display, Formatter};
|
use std::fmt::{Display, Formatter};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use derive_more::{Deref, From, Into};
|
||||||
use rgb::{RGB8, RGBA8};
|
use rgb::{RGB8, RGBA8};
|
||||||
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
use serde_with::{DeserializeFromStr, SerializeDisplay};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
#[error("The input value does not match the required format.")]
|
#[error("The input value does not match the required format.")]
|
||||||
pub struct RGBParsingError {}
|
pub struct ParsingError {}
|
||||||
|
|
||||||
pub fn parse_rgb8_from_hex_str(s: &str) -> Result<RGB8, RGBParsingError> {
|
fn parse_rgb8_from_hex_str(s: &str) -> Result<RGB8, ParsingError> {
|
||||||
let first_index = if s.starts_with('#') { 1 } else { 0 };
|
let first_index = if s.starts_with('#') { 1 } else { 0 };
|
||||||
if s.len() - first_index == 6 {
|
if s.len() - first_index == 6 {
|
||||||
let r = u8::from_str_radix(&s[first_index..(first_index + 2)], 16).map_err(|_| RGBParsingError {})?;
|
let r = u8::from_str_radix(&s[first_index..(first_index + 2)], 16).map_err(|_| ParsingError {})?;
|
||||||
let g = u8::from_str_radix(&s[(first_index + 2)..(first_index + 4)], 16).map_err(|_| RGBParsingError {})?;
|
let g = u8::from_str_radix(&s[(first_index + 2)..(first_index + 4)], 16).map_err(|_| ParsingError {})?;
|
||||||
let b = u8::from_str_radix(&s[(first_index + 4)..(first_index + 6)], 16).map_err(|_| RGBParsingError {})?;
|
let b = u8::from_str_radix(&s[(first_index + 4)..(first_index + 6)], 16).map_err(|_| ParsingError {})?;
|
||||||
|
|
||||||
return Ok(RGB8::new(r, g, b));
|
return Ok(RGB8::new(r, g, b));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(RGBParsingError {})
|
Err(ParsingError {})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_rgb8_as_hex_string(v: &RGB8, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt_rgb8_as_hex_string(v: &RGB8, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_fmt(format_args!("{:#04x}{:#04x}{:#04x}", v.r, v.g, v.b))
|
f.write_fmt(format_args!("{:#04x}{:#04x}{:#04x}", v.r, v.g, v.b))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_rgba8_from_hex_str(s: &str) -> Result<RGBA8, RGBParsingError> {
|
fn parse_rgba8_from_hex_str(s: &str) -> Result<RGBA8, ParsingError> {
|
||||||
let first_index = if s.starts_with('#') { 1 } else { 0 };
|
let first_index = if s.starts_with('#') { 1 } else { 0 };
|
||||||
if s.len() - first_index == 8 {
|
if s.len() - first_index == 8 {
|
||||||
let r = u8::from_str_radix(&s[first_index..(first_index + 2)], 16).map_err(|_| RGBParsingError {})?;
|
let r = u8::from_str_radix(&s[first_index..(first_index + 2)], 16).map_err(|_| ParsingError {})?;
|
||||||
let g = u8::from_str_radix(&s[(first_index + 2)..(first_index + 4)], 16).map_err(|_| RGBParsingError {})?;
|
let g = u8::from_str_radix(&s[(first_index + 2)..(first_index + 4)], 16).map_err(|_| ParsingError {})?;
|
||||||
let b = u8::from_str_radix(&s[(first_index + 4)..(first_index + 6)], 16).map_err(|_| RGBParsingError {})?;
|
let b = u8::from_str_radix(&s[(first_index + 4)..(first_index + 6)], 16).map_err(|_| ParsingError {})?;
|
||||||
let a = u8::from_str_radix(&s[(first_index + 6)..(first_index + 8)], 16).map_err(|_| RGBParsingError {})?;
|
let a = u8::from_str_radix(&s[(first_index + 6)..(first_index + 8)], 16).map_err(|_| ParsingError {})?;
|
||||||
|
|
||||||
return Ok(RGBA8::new(r, g, b, a));
|
return Ok(RGBA8::new(r, g, b, a));
|
||||||
}
|
}
|
||||||
|
|
||||||
Err(RGBParsingError {})
|
Err(ParsingError {})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fmt_rgba8_as_hex_string(v: &RGBA8, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt_rgba8_as_hex_string(v: &RGBA8, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
f.write_fmt(format_args!("{:#04x}{:#04x}{:#04x}{:#04x}", v.r, v.g, v.b, v.a))
|
f.write_fmt(format_args!("{:#04x}{:#04x}{:#04x}{:#04x}", v.r, v.g, v.b, v.a))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn parse_rgb8_with_optional_alpha_from_hex_str(s: &str, fallback_alpha: u8) -> Result<RGBA8, RGBParsingError> {
|
fn parse_rgb8_with_optional_alpha_from_hex_str(s: &str, fallback_alpha: u8) -> Result<RGBA8, ParsingError> {
|
||||||
// optionally +1 for the '#'
|
// optionally +1 for the '#'
|
||||||
match s.len() {
|
match s.len() {
|
||||||
6 | 7 => Ok(parse_rgb8_from_hex_str(s)?.alpha(fallback_alpha)),
|
6 | 7 => Ok(parse_rgb8_from_hex_str(s)?.alpha(fallback_alpha)),
|
||||||
8 | 9 => Ok(parse_rgba8_from_hex_str(s)?),
|
8 | 9 => Ok(parse_rgba8_from_hex_str(s)?),
|
||||||
_ => Err(RGBParsingError {}),
|
_ => Err(ParsingError {}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, SerializeDisplay, DeserializeFromStr)]
|
#[derive(Debug, Copy, Clone, PartialEq, Hash, Deref, From, Into, SerializeDisplay, DeserializeFromStr)]
|
||||||
pub struct SerializableRGB8(pub RGB8);
|
pub struct RGB8Wrapper(RGB8);
|
||||||
|
|
||||||
impl FromStr for SerializableRGB8 {
|
impl FromStr for RGB8Wrapper {
|
||||||
type Err = RGBParsingError;
|
type Err = ParsingError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
parse_rgb8_from_hex_str(s).map(SerializableRGB8)
|
parse_rgb8_from_hex_str(s).map(RGB8Wrapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SerializableRGB8 {
|
impl Display for RGB8Wrapper {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
fmt_rgb8_as_hex_string(&self.0, f)
|
fmt_rgb8_as_hex_string(&self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, SerializeDisplay, DeserializeFromStr)]
|
#[derive(Debug, Copy, Clone, PartialEq, Hash, Deref, From, Into, SerializeDisplay, DeserializeFromStr)]
|
||||||
pub struct SerializableRGBA8(pub RGBA8);
|
#[repr(transparent)]
|
||||||
|
pub struct RGBA8Wrapper(pub RGBA8);
|
||||||
|
|
||||||
impl FromStr for SerializableRGBA8 {
|
impl FromStr for RGBA8Wrapper {
|
||||||
type Err = RGBParsingError;
|
type Err = ParsingError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
parse_rgba8_from_hex_str(s).map(SerializableRGBA8)
|
parse_rgba8_from_hex_str(s).map(RGBA8Wrapper)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SerializableRGBA8 {
|
impl Display for RGBA8Wrapper {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
fmt_rgba8_as_hex_string(&self.0, f)
|
fmt_rgba8_as_hex_string(&self.0, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, SerializeDisplay, DeserializeFromStr)]
|
#[derive(Debug, Copy, Clone, PartialEq, Hash, Deref, From, Into, SerializeDisplay, DeserializeFromStr)]
|
||||||
pub struct SerializableRGB8WithOptionalAlpha(pub RGBA8);
|
#[repr(transparent)]
|
||||||
|
pub struct RGB8WithOptionalA(RGBA8);
|
||||||
|
|
||||||
impl FromStr for SerializableRGB8WithOptionalAlpha {
|
impl FromStr for RGB8WithOptionalA {
|
||||||
type Err = RGBParsingError;
|
type Err = ParsingError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
parse_rgb8_with_optional_alpha_from_hex_str(s, 0xff).map(SerializableRGB8WithOptionalAlpha)
|
parse_rgb8_with_optional_alpha_from_hex_str(s, 0xff).map(RGB8WithOptionalA)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for SerializableRGB8WithOptionalAlpha {
|
impl Display for RGB8WithOptionalA {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
fmt_rgba8_as_hex_string(&self.0, f)
|
fmt_rgba8_as_hex_string(&self.0, f)
|
||||||
}
|
}
|
||||||
|
|
|
@ -258,7 +258,7 @@ fn get_correct_button_color(config: &model::config::Config, state: &mut State, b
|
||||||
if key_page == &state.active_key_page_id {
|
if key_page == &state.active_key_page_id {
|
||||||
if let Some(knob_page) = &button_config.knob_page {
|
if let Some(knob_page) = &button_config.knob_page {
|
||||||
if knob_page == &state.active_knob_page_id {
|
if knob_page == &state.active_knob_page_id {
|
||||||
return config.active_button_color.0;
|
return config.active_button_color.into();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +266,7 @@ fn get_correct_button_color(config: &model::config::Config, state: &mut State, b
|
||||||
return RGB8::new(0, 0, 0);
|
return RGB8::new(0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
config.inactive_button_color.0
|
config.inactive_button_color.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_key_index_for_position(key_grid: &LoupedeckDeviceKeyGridCharacteristics, position: KeyPosition) -> Option<u8> {
|
fn get_key_index_for_position(key_grid: &LoupedeckDeviceKeyGridCharacteristics, position: KeyPosition) -> Option<u8> {
|
||||||
|
|
Loading…
Add table
Reference in a new issue