diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md
index 582645a7..8eba3098 100644
--- a/.github/ISSUE_TEMPLATE/bug-report.md
+++ b/.github/ISSUE_TEMPLATE/bug-report.md
@@ -22,25 +22,28 @@ If this is a regression, please mention which version was working previously.
**Kernel version**:
**VR Runtime**:
-- [ ] Monado/WiVRn
-- [ ] SteamVR/ALVR
+- [ ] Monado
+- [ ] WiVRn
+- [ ] ALVR
+- [ ] SteamLink
+- [ ] SteamVR (not SteamLink)
**GPU models and driver versions**:
## Overlay Logs
-
diff --git a/.github/workflows/build-all-features.yml b/.github/workflows/build-all-features.yml
index ea748df3..81267f63 100644
--- a/.github/workflows/build-all-features.yml
+++ b/.github/workflows/build-all-features.yml
@@ -11,7 +11,7 @@ env:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/.github/workflows/build-appimage.yml b/.github/workflows/build-appimage.yml
index 826fc778..4776d417 100644
--- a/.github/workflows/build-appimage.yml
+++ b/.github/workflows/build-appimage.yml
@@ -1,7 +1,6 @@
name: Build AppImage
on:
- workflow_dispatch:
push:
branches:
- 'main'
diff --git a/.github/workflows/build-default.yml b/.github/workflows/build-default.yml
index a20e5dfa..3ad4d140 100644
--- a/.github/workflows/build-default.yml
+++ b/.github/workflows/build-default.yml
@@ -11,7 +11,7 @@ env:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/.github/workflows/build-wayland-openvr.yml b/.github/workflows/build-wayland-openvr.yml
index 9b540dc5..dd3beb57 100644
--- a/.github/workflows/build-wayland-openvr.yml
+++ b/.github/workflows/build-wayland-openvr.yml
@@ -11,7 +11,7 @@ env:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/.github/workflows/build-wayland-openxr.yml b/.github/workflows/build-wayland-openxr.yml
index 42a5c33c..9ff5c9d4 100644
--- a/.github/workflows/build-wayland-openxr.yml
+++ b/.github/workflows/build-wayland-openxr.yml
@@ -11,7 +11,7 @@ env:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/.github/workflows/build-x11-openvr.yml b/.github/workflows/build-x11-openvr.yml
index cacd5e5c..2d54b81f 100644
--- a/.github/workflows/build-x11-openvr.yml
+++ b/.github/workflows/build-x11-openvr.yml
@@ -11,7 +11,7 @@ env:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/.github/workflows/build-x11-openxr.yml b/.github/workflows/build-x11-openxr.yml
index 7e045dee..697f0806 100644
--- a/.github/workflows/build-x11-openxr.yml
+++ b/.github/workflows/build-x11-openxr.yml
@@ -11,7 +11,7 @@ env:
jobs:
build:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/.github/workflows/make-release.yml b/.github/workflows/make-release.yml
index 3225e372..01286bc0 100644
--- a/.github/workflows/make-release.yml
+++ b/.github/workflows/make-release.yml
@@ -13,7 +13,7 @@ env:
jobs:
make_release:
- runs-on: ubuntu-22.04
+ runs-on: ubuntu-24.04
defaults:
run:
working-directory: ./wayvr
diff --git a/.github/workflows/scripts/appimage_prepare_env.sh b/.github/workflows/scripts/appimage_prepare_env.sh
index 7b7f14f3..857d81c7 100755
--- a/.github/workflows/scripts/appimage_prepare_env.sh
+++ b/.github/workflows/scripts/appimage_prepare_env.sh
@@ -1,7 +1,5 @@
#!/bin/sh
-sudo add-apt-repository -syn universe
-sudo add-apt-repository -syn ppa:pipewire-debian/pipewire-upstream || sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 25088A0359807596
sudo apt-get update
sudo apt-get install -y fuse cmake pkg-config fontconfig libasound2-dev libxkbcommon-dev libxkbcommon-x11-0 libxkbcommon-x11-dev libopenxr-dev libfontconfig-dev libdbus-1-dev libpipewire-0.3-0 libpipewire-0.3-dev libspa-0.2-dev libx11-6 libxext6 libxrandr2 libx11-dev libxext-dev libxrandr-dev libopenvr-dev libopenvr-api1 libwayland-dev libegl-dev libxcb-glx0 libxcb-glx0-dev
rustup update
diff --git a/Cargo.lock b/Cargo.lock
index 89da8af5..0be28f52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -33,17 +33,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
-[[package]]
-name = "aes"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
-dependencies = [
- "cfg-if",
- "cipher",
- "cpufeatures 0.2.17",
-]
-
[[package]]
name = "ahash"
version = "0.8.12"
@@ -154,6 +143,16 @@ dependencies = [
"libc",
]
+[[package]]
+name = "annotate-snippets"
+version = "0.11.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4"
+dependencies = [
+ "anstyle",
+ "unicode-width 0.2.2",
+]
+
[[package]]
name = "anstream"
version = "0.6.21"
@@ -206,9 +205,9 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.102"
+version = "1.0.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
+checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
[[package]]
name = "appendlist"
@@ -244,27 +243,6 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
-[[package]]
-name = "arboard"
-version = "3.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
-dependencies = [
- "clipboard-win",
- "image",
- "log",
- "objc2 0.6.3",
- "objc2-app-kit 0.3.2",
- "objc2-core-foundation",
- "objc2-core-graphics",
- "objc2-foundation 0.3.2",
- "parking_lot",
- "percent-encoding",
- "windows-sys 0.60.2",
- "wl-clipboard-rs",
- "x11rb",
-]
-
[[package]]
name = "arg_enum_proc_macro"
version = "0.3.4"
@@ -697,12 +675,6 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
-[[package]]
-name = "base64ct"
-version = "1.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
-
[[package]]
name = "bcdec_rs"
version = "0.2.0"
@@ -715,6 +687,7 @@ version = "0.72.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895"
dependencies = [
+ "annotate-snippets",
"bitflags 2.10.0",
"cexpr",
"clang-sys",
@@ -838,37 +811,6 @@ version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
-[[package]]
-name = "bzip2"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c"
-dependencies = [
- "libbz2-rs-sys",
-]
-
-[[package]]
-name = "cached-path"
-version = "0.10.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a9e79c94134331f0107c9f5b7caaf15f118ec91de67dcd3fa1145921e9fc27fa"
-dependencies = [
- "flate2",
- "fs2",
- "glob",
- "infer",
- "log",
- "rand 0.10.0",
- "reqwest",
- "serde",
- "serde_json",
- "sha2",
- "tar",
- "tempfile",
- "thiserror 2.0.17",
- "zip",
-]
-
[[package]]
name = "calloop"
version = "0.13.0"
@@ -947,22 +889,11 @@ dependencies = [
"nom 7.1.3",
]
-[[package]]
-name = "cfb"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f"
-dependencies = [
- "byteorder",
- "fnv",
- "uuid",
-]
-
[[package]]
name = "cfg-expr"
-version = "0.15.8"
+version = "0.20.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02"
+checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368"
dependencies = [
"smallvec",
"target-lexicon",
@@ -990,17 +921,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "chacha20"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.3.0",
- "rand_core 0.10.0",
-]
-
[[package]]
name = "chrono"
version = "0.4.42"
@@ -1025,16 +945,6 @@ dependencies = [
"phf",
]
-[[package]]
-name = "cipher"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
-dependencies = [
- "crypto-common",
- "inout",
-]
-
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -1086,15 +996,6 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
-[[package]]
-name = "clipboard-win"
-version = "5.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
-dependencies = [
- "error-code",
-]
-
[[package]]
name = "cmake"
version = "0.1.57"
@@ -1161,7 +1062,7 @@ dependencies = [
"serde-untagged",
"serde_core",
"serde_json",
- "toml 0.9.10+spec-1.1.0",
+ "toml",
"winnow",
"yaml-rust2",
]
@@ -1186,12 +1087,6 @@ dependencies = [
"tiny-keccak",
]
-[[package]]
-name = "constant_time_eq"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
-
[[package]]
name = "convert_case"
version = "0.4.0"
@@ -1212,9 +1107,6 @@ name = "cookie-factory"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2"
-dependencies = [
- "futures",
-]
[[package]]
name = "core-foundation"
@@ -1347,15 +1239,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "cpufeatures"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
-dependencies = [
- "libc",
-]
-
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -1548,6 +1431,7 @@ dependencies = [
"smol",
"smol-hyper",
"strum",
+ "uuid",
"wayvr-ipc",
"wgui",
"wlx-common",
@@ -1589,22 +1473,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "deflate64"
-version = "0.1.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "807800ff3288b621186fe0a8f3392c4652068257302709c24efd918c3dffcdc2"
-
-[[package]]
-name = "der"
-version = "0.7.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
-dependencies = [
- "pem-rfc7468",
- "zeroize",
-]
-
[[package]]
name = "deranged"
version = "0.5.5"
@@ -1635,7 +1503,6 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
- "subtle",
]
[[package]]
@@ -1887,12 +1754,6 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-name = "error-code"
-version = "3.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
-
[[package]]
name = "etagere"
version = "0.2.15"
@@ -1954,26 +1815,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
-[[package]]
-name = "fax"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
-dependencies = [
- "fax_derive",
-]
-
-[[package]]
-name = "fax_derive"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.113",
-]
-
[[package]]
name = "fdeflate"
version = "0.3.7"
@@ -1983,17 +1824,6 @@ dependencies = [
"simd-adler32",
]
-[[package]]
-name = "filetime"
-version = "0.2.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db"
-dependencies = [
- "cfg-if",
- "libc",
- "libredox",
-]
-
[[package]]
name = "find-msvc-tools"
version = "0.1.6"
@@ -2019,7 +1849,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb"
dependencies = [
"crc32fast",
- "libz-rs-sys",
"miniz_oxide",
]
@@ -2130,22 +1959,6 @@ dependencies = [
"percent-encoding",
]
-[[package]]
-name = "fs2"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
-dependencies = [
- "libc",
- "winapi",
-]
-
-[[package]]
-name = "fst"
-version = "0.4.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7ab85b9b05e3978cc9a9cf8fea7f01b494e1a09ed3037e16ba39edc7a29eb61a"
-
[[package]]
name = "futures"
version = "0.3.31"
@@ -2298,26 +2111,10 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
dependencies = [
"cfg-if",
"libc",
- "r-efi 5.3.0",
+ "r-efi",
"wasip2",
]
-[[package]]
-name = "getrandom"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
-dependencies = [
- "cfg-if",
- "js-sys",
- "libc",
- "r-efi 6.0.0",
- "rand_core 0.10.0",
- "wasip2",
- "wasip3",
- "wasm-bindgen",
-]
-
[[package]]
name = "gif"
version = "0.14.1"
@@ -2474,21 +2271,6 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest",
-]
-
-[[package]]
-name = "hmac-sha256"
-version = "1.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec9d92d097f4749b64e8cc33d924d9f40a2d4eb91402b458014b781f5733d60f"
-
[[package]]
name = "hound"
version = "3.5.1"
@@ -2562,45 +2344,6 @@ dependencies = [
"want",
]
-[[package]]
-name = "hyper-tls"
-version = "0.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
-dependencies = [
- "bytes",
- "http-body-util",
- "hyper",
- "hyper-util",
- "native-tls",
- "tokio",
- "tokio-native-tls",
- "tower-service",
-]
-
-[[package]]
-name = "hyper-util"
-version = "0.1.20"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
-dependencies = [
- "base64",
- "bytes",
- "futures-channel",
- "futures-util",
- "http",
- "http-body",
- "hyper",
- "ipnet",
- "libc",
- "percent-encoding",
- "pin-project-lite",
- "socket2",
- "tokio",
- "tower-service",
- "tracing",
-]
-
[[package]]
name = "iana-time-zone"
version = "0.1.64"
@@ -2706,12 +2449,6 @@ dependencies = [
"zerovec",
]
-[[package]]
-name = "id-arena"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
-
[[package]]
name = "ident_case"
version = "1.0.1"
@@ -2789,9 +2526,8 @@ dependencies = [
"png 0.18.0",
"ravif",
"rayon",
- "tiff",
- "zune-core 0.5.0",
- "zune-jpeg 0.5.8",
+ "zune-core",
+ "zune-jpeg",
]
[[package]]
@@ -2848,8 +2584,6 @@ checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
dependencies = [
"equivalent",
"hashbrown 0.16.1",
- "serde",
- "serde_core",
]
[[package]]
@@ -2858,24 +2592,6 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa799dd5ed20a7e349f3b4639aa80d74549c81716d9ec4f994c9b5815598306"
-[[package]]
-name = "infer"
-version = "0.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7"
-dependencies = [
- "cfb",
-]
-
-[[package]]
-name = "inout"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
-dependencies = [
- "generic-array",
-]
-
[[package]]
name = "input-linux"
version = "0.7.1"
@@ -2922,22 +2638,6 @@ dependencies = [
"windows-sys 0.52.0",
]
-[[package]]
-name = "ipnet"
-version = "2.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2"
-
-[[package]]
-name = "iri-string"
-version = "0.7.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a"
-dependencies = [
- "memchr",
- "serde",
-]
-
[[package]]
name = "is_terminal_polyfill"
version = "1.70.2"
@@ -3104,24 +2804,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-[[package]]
-name = "leb128fmt"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
-
[[package]]
name = "lebe"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
-[[package]]
-name = "libbz2-rs-sys"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7"
-
[[package]]
name = "libc"
version = "0.2.179"
@@ -3201,39 +2889,29 @@ dependencies = [
[[package]]
name = "libspa"
-version = "0.8.0"
-source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
+version = "0.9.2"
+source = "git+https://gitlab.freedesktop.org/pipewire/pipewire-rs.git?rev=670b0aabf04263ece5dc1e752e5d64aeae3056e8#670b0aabf04263ece5dc1e752e5d64aeae3056e8"
dependencies = [
"bitflags 2.10.0",
"cc",
- "convert_case 0.6.0",
"cookie-factory",
"libc",
"libspa-sys",
- "nix 0.27.1",
- "nom 7.1.3",
+ "nom 8.0.0",
+ "rustix 1.1.3",
"system-deps",
]
[[package]]
name = "libspa-sys"
-version = "0.8.0"
-source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
+version = "0.9.2"
+source = "git+https://gitlab.freedesktop.org/pipewire/pipewire-rs.git?rev=670b0aabf04263ece5dc1e752e5d64aeae3056e8#670b0aabf04263ece5dc1e752e5d64aeae3056e8"
dependencies = [
"bindgen",
"cc",
"system-deps",
]
-[[package]]
-name = "libz-rs-sys"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c10501e7805cee23da17c7790e59df2870c0d4043ec6d03f67d31e2b53e77415"
-dependencies = [
- "zlib-rs",
-]
-
[[package]]
name = "linebender_resource_handle"
version = "0.1.1"
@@ -3310,21 +2988,6 @@ dependencies = [
"hashbrown 0.16.1",
]
-[[package]]
-name = "lzma-rust2"
-version = "0.15.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1670343e58806300d87950e3401e820b519b9384281bbabfb15e3636689ffd69"
-
-[[package]]
-name = "lzma-rust2"
-version = "0.16.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47bb1e988e6fb779cf720ad431242d3f03167c1b3f2b1aae7f1a94b2495b36ae"
-dependencies = [
- "sha2",
-]
-
[[package]]
name = "mach2"
version = "0.4.3"
@@ -3340,17 +3003,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9"
dependencies = [
- "regex-automata 0.4.13",
-]
-
-[[package]]
-name = "matrixmultiply"
-version = "0.3.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08"
-dependencies = [
- "autocfg",
- "rawpointer",
+ "regex-automata",
]
[[package]]
@@ -3369,16 +3022,6 @@ version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
-[[package]]
-name = "memmap"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
-dependencies = [
- "libc",
- "winapi",
-]
-
[[package]]
name = "memmap2"
version = "0.9.9"
@@ -3495,21 +3138,6 @@ dependencies = [
"tempfile",
]
-[[package]]
-name = "ndarray"
-version = "0.17.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d"
-dependencies = [
- "matrixmultiply",
- "num-complex",
- "num-integer",
- "num-traits",
- "portable-atomic",
- "portable-atomic-util",
- "rawpointer",
-]
-
[[package]]
name = "ndk"
version = "0.9.0"
@@ -3546,17 +3174,6 @@ version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
-[[package]]
-name = "nix"
-version = "0.27.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
-dependencies = [
- "bitflags 2.10.0",
- "cfg-if",
- "libc",
-]
-
[[package]]
name = "nix"
version = "0.29.0"
@@ -3644,15 +3261,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "num-complex"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
-dependencies = [
- "num-traits",
-]
-
[[package]]
name = "num-conv"
version = "0.2.0"
@@ -3762,18 +3370,6 @@ dependencies = [
"objc2-quartz-core 0.2.2",
]
-[[package]]
-name = "objc2-app-kit"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
-dependencies = [
- "bitflags 2.10.0",
- "objc2 0.6.3",
- "objc2-core-graphics",
- "objc2-foundation 0.3.2",
-]
-
[[package]]
name = "objc2-audio-toolbox"
version = "0.3.2"
@@ -3858,19 +3454,6 @@ dependencies = [
"objc2 0.6.3",
]
-[[package]]
-name = "objc2-core-graphics"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
-dependencies = [
- "bitflags 2.10.0",
- "dispatch2",
- "objc2 0.6.3",
- "objc2-core-foundation",
- "objc2-io-surface",
-]
-
[[package]]
name = "objc2-core-image"
version = "0.2.2"
@@ -3935,17 +3518,6 @@ dependencies = [
"objc2-core-foundation",
]
-[[package]]
-name = "objc2-io-surface"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
-dependencies = [
- "bitflags 2.10.0",
- "objc2 0.6.3",
- "objc2-core-foundation",
-]
-
[[package]]
name = "objc2-link-presentation"
version = "0.2.2"
@@ -3954,7 +3526,7 @@ checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398"
dependencies = [
"block2",
"objc2 0.5.2",
- "objc2-app-kit 0.2.2",
+ "objc2-app-kit",
"objc2-foundation 0.2.2",
]
@@ -4179,40 +3751,6 @@ dependencies = [
"pin-project-lite",
]
-[[package]]
-name = "ort"
-version = "2.0.0-rc.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7de3af33d24a745ffb8fab904b13478438d1cd52868e6f17735ef6e1f8bf133"
-dependencies = [
- "ndarray",
- "ort-sys",
- "smallvec",
- "tracing",
- "ureq",
-]
-
-[[package]]
-name = "ort-sys"
-version = "2.0.0-rc.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7b497d21a8b6fbb4b5a544f8fadb77e801a09ae0add9e411d31c6f89e3c1e90"
-dependencies = [
- "hmac-sha256",
- "lzma-rust2 0.15.7",
- "ureq",
-]
-
-[[package]]
-name = "os_pipe"
-version = "1.2.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
-dependencies = [
- "libc",
- "windows-sys 0.61.2",
-]
-
[[package]]
name = "ouroboros"
version = "0.18.5"
@@ -4320,25 +3858,6 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
-[[package]]
-name = "pbkdf2"
-version = "0.12.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
-dependencies = [
- "digest",
- "hmac",
-]
-
-[[package]]
-name = "pem-rfc7468"
-version = "0.7.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
-dependencies = [
- "base64ct",
-]
-
[[package]]
name = "percent-encoding"
version = "2.3.2"
@@ -4468,24 +3987,21 @@ dependencies = [
[[package]]
name = "pipewire"
-version = "0.8.0"
-source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
+version = "0.9.2"
+source = "git+https://gitlab.freedesktop.org/pipewire/pipewire-rs.git?rev=670b0aabf04263ece5dc1e752e5d64aeae3056e8#670b0aabf04263ece5dc1e752e5d64aeae3056e8"
dependencies = [
- "anyhow",
"bitflags 2.10.0",
"libc",
"libspa",
"libspa-sys",
- "nix 0.27.1",
- "once_cell",
"pipewire-sys",
- "thiserror 1.0.69",
+ "rustix 1.1.3",
]
[[package]]
name = "pipewire-sys"
-version = "0.8.0"
-source = "git+https://gitlab.freedesktop.org/galister/pipewire-rs.git?rev=6ef2cd1af41e5e8844c63c37ea9a01f0758079b0#6ef2cd1af41e5e8844c63c37ea9a01f0758079b0"
+version = "0.9.2"
+source = "git+https://gitlab.freedesktop.org/pipewire/pipewire-rs.git?rev=670b0aabf04263ece5dc1e752e5d64aeae3056e8#670b0aabf04263ece5dc1e752e5d64aeae3056e8"
dependencies = [
"bindgen",
"libspa-sys",
@@ -4568,12 +4084,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
-[[package]]
-name = "ppmd-rust"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24"
-
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@@ -4599,7 +4109,7 @@ version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983"
dependencies = [
- "toml_edit 0.23.10+spec-1.0.0",
+ "toml_edit",
]
[[package]]
@@ -4779,12 +4289,6 @@ version = "5.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
-[[package]]
-name = "r-efi"
-version = "6.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
-
[[package]]
name = "rand"
version = "0.7.3"
@@ -4820,17 +4324,6 @@ dependencies = [
"rand_core 0.9.3",
]
-[[package]]
-name = "rand"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8"
-dependencies = [
- "chacha20",
- "getrandom 0.4.2",
- "rand_core 0.10.0",
-]
-
[[package]]
name = "rand_chacha"
version = "0.2.2"
@@ -4888,12 +4381,6 @@ dependencies = [
"getrandom 0.3.4",
]
-[[package]]
-name = "rand_core"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
-
[[package]]
name = "rand_hc"
version = "0.2.0"
@@ -4995,12 +4482,6 @@ dependencies = [
"objc2-quartz-core 0.3.2",
]
-[[package]]
-name = "rawpointer"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
-
[[package]]
name = "rayon"
version = "1.11.0"
@@ -5067,24 +4548,14 @@ dependencies = [
[[package]]
name = "regex"
-version = "1.12.3"
+version = "1.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4"
dependencies = [
"aho-corasick",
"memchr",
- "regex-automata 0.4.13",
- "regex-syntax 0.8.8",
-]
-
-[[package]]
-name = "regex-automata"
-version = "0.1.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
-dependencies = [
- "fst",
- "regex-syntax 0.6.29",
+ "regex-automata",
+ "regex-syntax",
]
[[package]]
@@ -5095,59 +4566,15 @@ checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
dependencies = [
"aho-corasick",
"memchr",
- "regex-syntax 0.8.8",
+ "regex-syntax",
]
-[[package]]
-name = "regex-syntax"
-version = "0.6.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
-
[[package]]
name = "regex-syntax"
version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
-[[package]]
-name = "reqwest"
-version = "0.12.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147"
-dependencies = [
- "base64",
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "http",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-tls",
- "hyper-util",
- "js-sys",
- "log",
- "native-tls",
- "percent-encoding",
- "pin-project-lite",
- "rustls-pki-types",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper",
- "tokio",
- "tokio-native-tls",
- "tower",
- "tower-http",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
-]
-
[[package]]
name = "resvg"
version = "0.45.1"
@@ -5330,15 +4757,6 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-name = "rustls-pki-types"
-version = "1.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd"
-dependencies = [
- "zeroize",
-]
-
[[package]]
name = "rustversion"
version = "1.0.22"
@@ -5521,15 +4939,6 @@ dependencies = [
"syn 2.0.113",
]
-[[package]]
-name = "serde_spanned"
-version = "0.6.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
-dependencies = [
- "serde",
-]
-
[[package]]
name = "serde_spanned"
version = "1.0.4"
@@ -5539,18 +4948,6 @@ dependencies = [
"serde_core",
]
-[[package]]
-name = "serde_urlencoded"
-version = "0.7.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd"
-dependencies = [
- "form_urlencoded",
- "itoa",
- "ryu",
- "serde",
-]
-
[[package]]
name = "serde_yaml"
version = "0.9.34+deprecated"
@@ -5564,17 +4961,6 @@ dependencies = [
"unsafe-libyaml",
]
-[[package]]
-name = "sha1"
-version = "0.10.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.2.17",
- "digest",
-]
-
[[package]]
name = "sha2"
version = "0.10.9"
@@ -5582,7 +4968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
- "cpufeatures 0.2.17",
+ "cpufeatures",
"digest",
]
@@ -5870,17 +5256,6 @@ dependencies = [
"windows-sys 0.60.2",
]
-[[package]]
-name = "socks"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0c3dbbd9ae980613c6dd8e28a9407b50509d3803b57624d5dfe8315218cd58b"
-dependencies = [
- "byteorder",
- "libc",
- "winapi",
-]
-
[[package]]
name = "stable_deref_trait"
version = "1.2.1"
@@ -5929,28 +5304,6 @@ dependencies = [
"syn 2.0.113",
]
-[[package]]
-name = "subtle"
-version = "2.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
-
-[[package]]
-name = "super-swipe-type"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22126517bdf94f41b3d0af625fdf69b5ff131b7dd19a59866d3c00a56072af88"
-dependencies = [
- "anyhow",
- "cached-path",
- "fst",
- "memmap",
- "ort",
- "regex",
- "regex-automata 0.1.10",
- "vector2",
-]
-
[[package]]
name = "svg_fmt"
version = "0.4.5"
@@ -6049,15 +5402,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "sync_wrapper"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
-dependencies = [
- "futures-core",
-]
-
[[package]]
name = "synstructure"
version = "0.13.2"
@@ -6094,14 +5438,14 @@ dependencies = [
[[package]]
name = "system-deps"
-version = "6.2.2"
+version = "7.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349"
+checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f"
dependencies = [
"cfg-expr",
"heck 0.5.0",
"pkg-config",
- "toml 0.8.23",
+ "toml",
"version-compare",
]
@@ -6117,22 +5461,11 @@ dependencies = [
"slotmap",
]
-[[package]]
-name = "tar"
-version = "0.4.44"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a"
-dependencies = [
- "filetime",
- "libc",
- "xattr",
-]
-
[[package]]
name = "target-lexicon"
-version = "0.12.16"
+version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
+checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c"
[[package]]
name = "tempfile"
@@ -6205,20 +5538,6 @@ dependencies = [
"cfg-if",
]
-[[package]]
-name = "tiff"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
-dependencies = [
- "fax",
- "flate2",
- "half",
- "quick-error",
- "weezl",
- "zune-jpeg 0.4.21",
-]
-
[[package]]
name = "time"
version = "0.3.47"
@@ -6227,7 +5546,6 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
- "js-sys",
"num-conv",
"powerfmt",
"serde_core",
@@ -6337,16 +5655,6 @@ dependencies = [
"syn 2.0.113",
]
-[[package]]
-name = "tokio-native-tls"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
-dependencies = [
- "native-tls",
- "tokio",
-]
-
[[package]]
name = "tokio-util"
version = "0.7.18"
@@ -6360,40 +5668,21 @@ dependencies = [
"tokio",
]
-[[package]]
-name = "toml"
-version = "0.8.23"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
-dependencies = [
- "serde",
- "serde_spanned 0.6.9",
- "toml_datetime 0.6.11",
- "toml_edit 0.22.27",
-]
-
[[package]]
name = "toml"
version = "0.9.10+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0825052159284a1a8b4d6c0c86cbc801f2da5afd2b225fa548c72f2e74002f48"
dependencies = [
+ "indexmap 2.12.1",
"serde_core",
- "serde_spanned 1.0.4",
- "toml_datetime 0.7.5+spec-1.1.0",
+ "serde_spanned",
+ "toml_datetime",
"toml_parser",
+ "toml_writer",
"winnow",
]
-[[package]]
-name = "toml_datetime"
-version = "0.6.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
-dependencies = [
- "serde",
-]
-
[[package]]
name = "toml_datetime"
version = "0.7.5+spec-1.1.0"
@@ -6403,19 +5692,6 @@ dependencies = [
"serde_core",
]
-[[package]]
-name = "toml_edit"
-version = "0.22.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
-dependencies = [
- "indexmap 2.12.1",
- "serde",
- "serde_spanned 0.6.9",
- "toml_datetime 0.6.11",
- "winnow",
-]
-
[[package]]
name = "toml_edit"
version = "0.23.10+spec-1.0.0"
@@ -6423,7 +5699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269"
dependencies = [
"indexmap 2.12.1",
- "toml_datetime 0.7.5+spec-1.1.0",
+ "toml_datetime",
"toml_parser",
"winnow",
]
@@ -6438,49 +5714,10 @@ dependencies = [
]
[[package]]
-name = "tower"
-version = "0.5.3"
+name = "toml_writer"
+version = "1.1.1+spec-1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
-dependencies = [
- "futures-core",
- "futures-util",
- "pin-project-lite",
- "sync_wrapper",
- "tokio",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "tower-http"
-version = "0.6.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8"
-dependencies = [
- "bitflags 2.10.0",
- "bytes",
- "futures-util",
- "http",
- "http-body",
- "iri-string",
- "pin-project-lite",
- "tower",
- "tower-layer",
- "tower-service",
-]
-
-[[package]]
-name = "tower-layer"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e"
-
-[[package]]
-name = "tower-service"
-version = "0.3.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3"
+checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db"
[[package]]
name = "tracing"
@@ -6535,7 +5772,7 @@ dependencies = [
"matchers",
"nu-ansi-term",
"once_cell",
- "regex-automata 0.4.13",
+ "regex-automata",
"sharded-slab",
"smallvec",
"thread_local",
@@ -6544,17 +5781,6 @@ dependencies = [
"tracing-log",
]
-[[package]]
-name = "tree_magic_mini"
-version = "3.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8765b90061cba6c22b5831f675da109ae5561588290f9fa2317adab2714d5a6"
-dependencies = [
- "memchr",
- "nom 8.0.0",
- "petgraph",
-]
-
[[package]]
name = "try-lock"
version = "0.2.5"
@@ -6570,12 +5796,6 @@ dependencies = [
"core_maths",
]
-[[package]]
-name = "typed-path"
-version = "0.12.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e"
-
[[package]]
name = "typeid"
version = "1.0.3"
@@ -6664,48 +5884,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254"
-[[package]]
-name = "unicode-xid"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
-
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
-[[package]]
-name = "ureq"
-version = "3.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdc97a28575b85cfedf2a7e7d3cc64b3e11bd8ac766666318003abbacc7a21fc"
-dependencies = [
- "base64",
- "der",
- "log",
- "native-tls",
- "percent-encoding",
- "rustls-pki-types",
- "socks",
- "ureq-proto",
- "utf-8",
- "webpki-root-certs",
-]
-
-[[package]]
-name = "ureq-proto"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d81f9efa9df032be5934a46a068815a10a042b494b6a58cb0a1a97bb5467ed6f"
-dependencies = [
- "base64",
- "http",
- "httparse",
- "log",
-]
-
[[package]]
name = "url"
version = "2.5.7"
@@ -6740,12 +5924,6 @@ dependencies = [
"xmlwriter",
]
-[[package]]
-name = "utf-8"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
-
[[package]]
name = "utf8_iter"
version = "1.0.4"
@@ -6794,12 +5972,6 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-[[package]]
-name = "vector2"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2f562f8d4f41867568374e1a63bd12914795f558319f9bf9b0582c4d96a8de04"
-
[[package]]
name = "version-compare"
version = "0.2.1"
@@ -6913,16 +6085,7 @@ version = "1.0.1+wasi-0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
dependencies = [
- "wit-bindgen 0.46.0",
-]
-
-[[package]]
-name = "wasip3"
-version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
-dependencies = [
- "wit-bindgen 0.51.0",
+ "wit-bindgen",
]
[[package]]
@@ -6983,40 +6146,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "wasm-encoder"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
-dependencies = [
- "leb128fmt",
- "wasmparser",
-]
-
-[[package]]
-name = "wasm-metadata"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
-dependencies = [
- "anyhow",
- "indexmap 2.12.1",
- "wasm-encoder",
- "wasmparser",
-]
-
-[[package]]
-name = "wasmparser"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
-dependencies = [
- "bitflags 2.10.0",
- "hashbrown 0.15.5",
- "indexmap 2.12.1",
- "semver",
-]
-
[[package]]
name = "wayland-backend"
version = "0.3.12"
@@ -7173,7 +6302,6 @@ name = "wayvr"
version = "26.2.1"
dependencies = [
"anyhow",
- "arboard",
"ash",
"bytes",
"chrono",
@@ -7213,7 +6341,6 @@ dependencies = [
"smithay",
"smol",
"strum",
- "super-swipe-type",
"sysinfo",
"thiserror 2.0.17",
"tracing",
@@ -7282,15 +6409,6 @@ dependencies = [
"wasm-bindgen",
]
-[[package]]
-name = "webpki-root-certs"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca"
-dependencies = [
- "rustls-pki-types",
-]
-
[[package]]
name = "weezl"
version = "0.1.12"
@@ -7793,7 +6911,7 @@ dependencies = [
"memmap2",
"ndk",
"objc2 0.5.2",
- "objc2-app-kit 0.2.2",
+ "objc2-app-kit",
"objc2-foundation 0.2.2",
"objc2-ui-kit",
"orbclient",
@@ -7836,112 +6954,6 @@ version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
-[[package]]
-name = "wit-bindgen"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
-dependencies = [
- "wit-bindgen-rust-macro",
-]
-
-[[package]]
-name = "wit-bindgen-core"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
-dependencies = [
- "anyhow",
- "heck 0.5.0",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-bindgen-rust"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
-dependencies = [
- "anyhow",
- "heck 0.5.0",
- "indexmap 2.12.1",
- "prettyplease",
- "syn 2.0.113",
- "wasm-metadata",
- "wit-bindgen-core",
- "wit-component",
-]
-
-[[package]]
-name = "wit-bindgen-rust-macro"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
-dependencies = [
- "anyhow",
- "prettyplease",
- "proc-macro2",
- "quote",
- "syn 2.0.113",
- "wit-bindgen-core",
- "wit-bindgen-rust",
-]
-
-[[package]]
-name = "wit-component"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
-dependencies = [
- "anyhow",
- "bitflags 2.10.0",
- "indexmap 2.12.1",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "wasm-encoder",
- "wasm-metadata",
- "wasmparser",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap 2.12.1",
- "log",
- "semver",
- "serde",
- "serde_derive",
- "serde_json",
- "unicode-xid",
- "wasmparser",
-]
-
-[[package]]
-name = "wl-clipboard-rs"
-version = "0.9.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9651471a32e87d96ef3a127715382b2d11cc7c8bb9822ded8a7cc94072eb0a3"
-dependencies = [
- "libc",
- "log",
- "os_pipe",
- "rustix 1.1.3",
- "thiserror 2.0.17",
- "tree_magic_mini",
- "wayland-backend",
- "wayland-client",
- "wayland-protocols",
- "wayland-protocols-wlr",
-]
-
[[package]]
name = "wlx-capture"
version = "0.6.0"
@@ -8020,16 +7032,6 @@ version = "0.13.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
-[[package]]
-name = "xattr"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156"
-dependencies = [
- "libc",
- "rustix 1.1.3",
-]
-
[[package]]
name = "xcb"
version = "1.7.0"
@@ -8272,26 +7274,6 @@ dependencies = [
"synstructure",
]
-[[package]]
-name = "zeroize"
-version = "1.8.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.113",
-]
-
[[package]]
name = "zerotrie"
version = "0.2.3"
@@ -8325,91 +7307,12 @@ dependencies = [
"syn 2.0.113",
]
-[[package]]
-name = "zip"
-version = "8.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b680f2a0cd479b4cff6e1233c483fdead418106eae419dc60200ae9850f6d004"
-dependencies = [
- "aes",
- "bzip2",
- "constant_time_eq",
- "crc32fast",
- "deflate64",
- "flate2",
- "getrandom 0.4.2",
- "hmac",
- "indexmap 2.12.1",
- "lzma-rust2 0.16.2",
- "memchr",
- "pbkdf2",
- "ppmd-rust",
- "sha1",
- "time",
- "typed-path",
- "zeroize",
- "zopfli",
- "zstd",
-]
-
-[[package]]
-name = "zlib-rs"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40990edd51aae2c2b6907af74ffb635029d5788228222c4bb811e9351c0caad3"
-
[[package]]
name = "zmij"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30e0d8dffbae3d840f64bda38e28391faef673a7b5a6017840f2a106c8145868"
-[[package]]
-name = "zopfli"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249"
-dependencies = [
- "bumpalo",
- "crc32fast",
- "log",
- "simd-adler32",
-]
-
-[[package]]
-name = "zstd"
-version = "0.13.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
-dependencies = [
- "zstd-safe",
-]
-
-[[package]]
-name = "zstd-safe"
-version = "7.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d"
-dependencies = [
- "zstd-sys",
-]
-
-[[package]]
-name = "zstd-sys"
-version = "2.0.16+zstd.1.5.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748"
-dependencies = [
- "cc",
- "pkg-config",
-]
-
-[[package]]
-name = "zune-core"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
-
[[package]]
name = "zune-core"
version = "0.5.0"
@@ -8425,22 +7328,13 @@ dependencies = [
"simd-adler32",
]
-[[package]]
-name = "zune-jpeg"
-version = "0.4.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
-dependencies = [
- "zune-core 0.4.12",
-]
-
[[package]]
name = "zune-jpeg"
version = "0.5.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
dependencies = [
- "zune-core 0.5.0",
+ "zune-core",
]
[[package]]
diff --git a/Cargo.toml b/Cargo.toml
index 5488eca7..1550b9d5 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,3 +1,42 @@
+[workspace]
+resolver = "3"
+members = [
+ "dash-frontend",
+ "scripts/prost_build",
+ "uidev",
+ "wayvr",
+ "wayvr-ipc",
+ "wayvrctl",
+ "wgui",
+ "wlx-capture",
+ "wlx-common",
+]
+
+[workspace.dependencies]
+anyhow = "1.0.100"
+clap = { version = "4.5.53", features = ["derive"] }
+glam = { version = "0.30.9", features = ["mint", "serde"] }
+idmap = "0.2.2"
+idmap-derive = "0.2.22"
+log = "0.4.29"
+regex = "1.12.2"
+rust-embed = "8.9.0"
+serde = { version = "1", features = ["derive"] }
+serde_json = "1.0.145"
+slotmap = "1.1.1"
+smol = "2.0.2"
+strum = { version = "0.27.2", features = ["derive"] }
+uuid = { version = "1.19.0", features = ["fast-rng", "v4", "serde"] }
+vulkano = { version = "0.35.2", default-features = false, features = [
+ "macros",
+] }
+vulkano-shaders = "0.35.0"
+wayland-client = { version = "0.31.11" }
+xdg = "3.0.0"
+
+[patch.crates-io]
+vulkano = { git = "https://github.com/galister/vulkano.git", rev = "cf7f92867928a56ce16b376037c1120f2b167678" }
+
[profile.dev]
opt-level = 1
debug = true
@@ -19,41 +58,3 @@ incremental = true
[profile.release-with-debug]
inherits = "release"
debug = true
-
-[workspace]
-members = [
- "uidev",
- "wgui",
- "wlx-common",
- "wayvr",
- "wlx-capture",
- "dash-frontend",
- "wayvr-ipc",
- "wayvrctl",
- "scripts/prost_build",
-]
-resolver = "3"
-
-[patch.crates-io]
-vulkano = { git = "https://github.com/galister/vulkano.git", rev = "cf7f92867928a56ce16b376037c1120f2b167678" }
-
-[workspace.dependencies]
-anyhow = "1.0.100"
-smol = "2.0.2"
-glam = { version = "0.30.9", features = ["mint", "serde"] }
-clap = { version = "4.5.53", features = ["derive"] }
-xdg = "3.0.0"
-idmap = "0.2.2"
-idmap-derive = "0.2.22"
-log = "0.4.29"
-regex = "1.12.2"
-rust-embed = "8.9.0"
-serde = { version = "1", features = ["derive"] }
-serde_json = "1.0.145"
-slotmap = "1.1.1"
-strum = { version = "0.27.2", features = ["derive"] }
-vulkano = { version = "0.35.2", default-features = false, features = [
- "macros",
-] }
-vulkano-shaders = "0.35.0"
-wayland-client = { version = "0.31.11" }
diff --git a/dash-frontend/Cargo.toml b/dash-frontend/Cargo.toml
index 06f1b010..0f2a839f 100644
--- a/dash-frontend/Cargo.toml
+++ b/dash-frontend/Cargo.toml
@@ -8,26 +8,25 @@ authors = ["galister", "oo8dev"]
repository = "https://github.com/wlx-team/wayvr"
[dependencies]
-wayvr-ipc = { path = "../wayvr-ipc", default-features = false }
-wgui = { path = "../wgui/" }
-wlx-common = { path = "../wlx-common" }
-
anyhow.workspace = true
+async-native-tls = "0.5.0"
+chrono = "0.4.42"
glam = { workspace = true, features = ["mint", "serde"] }
+http-body-util = "0.1.3"
+hyper = { version = "1.8.1", features = ["client", "http1", "http2"] }
+keyvalues-parser = { git = "https://github.com/CosmicHorrorDev/vdf-rs.git", rev = "fc6dcbea9eb13cacb98dea40063f6f56cde6e145" }
log.workspace = true
-xdg.workspace = true
rust-embed.workspace = true
serde = { workspace = true, features = ["rc"] }
serde_json.workspace = true
-strum.workspace = true
-
-chrono = "0.4.42"
-keyvalues-parser = { git = "https://github.com/CosmicHorrorDev/vdf-rs.git", rev = "fc6dcbea9eb13cacb98dea40063f6f56cde6e145" }
smol = { workspace = true }
-hyper = { version = "1.8.1", features = ["client", "http1", "http2"] }
-http-body-util = "0.1.3"
-async-native-tls = "0.5.0"
smol-hyper = "0.1.1"
+strum.workspace = true
+uuid.workspace = true
+wayvr-ipc = { path = "../wayvr-ipc", default-features = false }
+wgui = { path = "../wgui/" }
+wlx-common = { path = "../wlx-common" }
+xdg.workspace = true
[features]
default = ["monado"]
diff --git a/dash-frontend/assets/dashboard/builtin-skybox-preview.jpg b/dash-frontend/assets/dashboard/builtin-skybox-preview.jpg
new file mode 100644
index 00000000..7ca3e583
Binary files /dev/null and b/dash-frontend/assets/dashboard/builtin-skybox-preview.jpg differ
diff --git a/dash-frontend/assets/dashboard/check.svg b/dash-frontend/assets/dashboard/check.svg
new file mode 100644
index 00000000..d4ba7af0
--- /dev/null
+++ b/dash-frontend/assets/dashboard/check.svg
@@ -0,0 +1 @@
+
diff --git a/dash-frontend/assets/dashboard/download.svg b/dash-frontend/assets/dashboard/download.svg
new file mode 100644
index 00000000..f446969b
--- /dev/null
+++ b/dash-frontend/assets/dashboard/download.svg
@@ -0,0 +1 @@
+
diff --git a/dash-frontend/assets/dashboard/error.svg b/dash-frontend/assets/dashboard/error.svg
new file mode 100644
index 00000000..fa2fe4b7
--- /dev/null
+++ b/dash-frontend/assets/dashboard/error.svg
@@ -0,0 +1 @@
+
diff --git a/dash-frontend/assets/dashboard/loading.svg b/dash-frontend/assets/dashboard/loading.svg
new file mode 100644
index 00000000..1c800c12
--- /dev/null
+++ b/dash-frontend/assets/dashboard/loading.svg
@@ -0,0 +1 @@
+
diff --git a/dash-frontend/assets/dashboard/trash.svg b/dash-frontend/assets/dashboard/trash.svg
new file mode 100644
index 00000000..914fd402
--- /dev/null
+++ b/dash-frontend/assets/dashboard/trash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/dash-frontend/assets/gui/dashboard.xml b/dash-frontend/assets/gui/dashboard.xml
index 7d5a9a4c..8f2b05f4 100644
--- a/dash-frontend/assets/gui/dashboard.xml
+++ b/dash-frontend/assets/gui/dashboard.xml
@@ -86,7 +86,8 @@
round="8"
flex_grow="1"
width="100%"
- overflow_y="scroll"
+ overflow="scroll"
+ position="relative"
>
+
+
+
+
+
+
+
+
+
+
diff --git a/dash-frontend/assets/gui/tab/monado_tab_general_settings.xml b/dash-frontend/assets/gui/tab/monado_tab_general_settings.xml
index 3d918b82..c99cbf21 100644
--- a/dash-frontend/assets/gui/tab/monado_tab_general_settings.xml
+++ b/dash-frontend/assets/gui/tab/monado_tab_general_settings.xml
@@ -29,7 +29,7 @@
-
+
diff --git a/dash-frontend/assets/gui/tab/settings.xml b/dash-frontend/assets/gui/tab/settings.xml
index 9ca21283..61583d75 100644
--- a/dash-frontend/assets/gui/tab/settings.xml
+++ b/dash-frontend/assets/gui/tab/settings.xml
@@ -48,6 +48,7 @@
+
@@ -59,4 +60,4 @@
-
\ No newline at end of file
+
diff --git a/dash-frontend/assets/gui/view/dialog_box.xml b/dash-frontend/assets/gui/view/dialog_box.xml
new file mode 100644
index 00000000..0c3f87cf
--- /dev/null
+++ b/dash-frontend/assets/gui/view/dialog_box.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/dash-frontend/assets/gui/view/download_file.xml b/dash-frontend/assets/gui/view/download_file.xml
new file mode 100644
index 00000000..63813dd7
--- /dev/null
+++ b/dash-frontend/assets/gui/view/download_file.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dash-frontend/assets/gui/view/popup_window.xml b/dash-frontend/assets/gui/view/popup_window.xml
index 1f1f6fed..e373a2f4 100644
--- a/dash-frontend/assets/gui/view/popup_window.xml
+++ b/dash-frontend/assets/gui/view/popup_window.xml
@@ -9,6 +9,7 @@
width="100%"
height="100%"
flex_direction="column"
+ position="absolute"
>
-
-
+ position="relative"
+ >
+
+
+
-
\ No newline at end of file
+
diff --git a/dash-frontend/assets/gui/view/remote_skymap_downloader.xml b/dash-frontend/assets/gui/view/remote_skymap_downloader.xml
new file mode 100644
index 00000000..c6e386ae
--- /dev/null
+++ b/dash-frontend/assets/gui/view/remote_skymap_downloader.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dash-frontend/assets/gui/view/remote_skymap_list.xml b/dash-frontend/assets/gui/view/remote_skymap_list.xml
new file mode 100644
index 00000000..e2f3603a
--- /dev/null
+++ b/dash-frontend/assets/gui/view/remote_skymap_list.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/dash-frontend/assets/gui/view/skymap_list.xml b/dash-frontend/assets/gui/view/skymap_list.xml
new file mode 100644
index 00000000..b32b0ec1
--- /dev/null
+++ b/dash-frontend/assets/gui/view/skymap_list.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
diff --git a/dash-frontend/assets/gui/view/skymap_list_cell.xml b/dash-frontend/assets/gui/view/skymap_list_cell.xml
new file mode 100644
index 00000000..93f32e8e
--- /dev/null
+++ b/dash-frontend/assets/gui/view/skymap_list_cell.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dash-frontend/assets/lang/en.json b/dash-frontend/assets/lang/en.json
index 7d5e2e05..7b1b1626 100644
--- a/dash-frontend/assets/lang/en.json
+++ b/dash-frontend/assets/lang/en.json
@@ -38,6 +38,8 @@
"BLOCK_GAME_INPUT_IGNORE_WATCH_HELP": "Do not block input when watch is hovered",
"BLOCK_POSES_ON_KBD_INTERACTION": "Block poses when interacting with keyboard",
"BLOCK_POSES_ON_KBD_INTERACTION_HELP": "Blocks the game from receiving poses when the keyboard is hovered and 'Block game input' is enabled",
+ "BROWSE_ONLINE_CATALOG": "Browse online catalog...",
+ "BROWSE_SKYMAPS": "Browse skymaps",
"CAPTURE_METHOD": "Wayland screen capture",
"CAPTURE_METHOD_HELP": "Try changing this if you are\nexperiencing black or glitchy screens",
"COLOR_KEYING": "Color keying",
@@ -64,14 +66,13 @@
"KEYBOARD_MIDDLE_CLICK": "Keyboard middle click",
"KEYBOARD_MIDDLE_CLICK_HELP": "Modifier to use when typing\nwith purple laser",
"KEYBOARD_SOUND_ENABLED": "Keyboard sounds",
- "KEYBOARD_SWIPE_TO_TYPE_ENABLED": "Keyboard swipe to type",
- "KEYBOARD_SWIPE_TO_TYPE_ENABLED_HELP": "Only works for English! Results will be drastically worse on non qwerty keyboards.",
"LANGUAGE": "Language",
"LEFT_HANDED_MOUSE": "Left-handed mouse",
"LEFT_HANDED_MOUSE_HELP": "Use this if mouse buttons are swapped",
"LONG_PRESS_DURATION": "Long press duration",
"LOOK_AND_FEEL": "Look & Feel",
"MISC": "Miscellaneous",
+ "NO_SKYMAPS_FOUND": "No skymaps found",
"NOTIFICATIONS_ENABLED": "Enable notifications",
"NOTIFICATIONS_SOUND_ENABLED": "Notification sounds",
"OPAQUE_BACKGROUND": "Opaque background",
@@ -98,7 +99,10 @@
"SCREEN_RENDER_DOWN": "Render screen at lower resolution",
"SCREEN_RENDER_DOWN_HELP": "Helps with aliasing on high-res screens",
"SCROLL_SPEED": "Scroll speed",
+ "SELECT_VARIANT": "Select variant",
"SETS_ON_WATCH": "Sets on watch",
+ "SKYBOX": "Skybox",
+ "SKYMAP_ALREADY_DOWNLOADED": "This skymap is already downloaded. Select desired action.",
"SPACE_DRAG_MULTIPLIER": "Space drag multiplier",
"SPACE_DRAG_UNLOCKED": "Allow space drag on all axes",
"SPACE_ROTATE_UNLOCKED": "Allow space rotate on all axes",
@@ -136,8 +140,12 @@
"VOLUME": "Volume"
},
"CLOSE_WINDOW": "Close window",
+ "CREATION_DATE": "Creation date",
"DEBUG_INFO": "Debug info",
"DISPLAY_BRIGHTNESS": "Display brightness",
+ "DOWNLOADER": "Downloader",
+ "DOWNLOAD_AGAIN": "Download again",
+ "DOWNLOADING_FILE": "Downloading file...",
"FAILED_TO_LAUNCH_APPLICATION": "Failed to launch a application:",
"GAME_LAUNCHED": "Game launched",
"GAME_LIST": {
@@ -151,7 +159,9 @@
"HELLO_USER": "Hello, {USER}!",
"HIDE": "Hide",
"HOME_SCREEN": "Home",
+ "MODIFICATION_DATE": "Modification date",
"MONADO_RUNTIME": "Monado runtime",
+ "LOADING": "Loading...",
"POPUP_ADD_DISPLAY": {
"RESOLUTION": "Resolution"
},
@@ -162,8 +172,12 @@
"PROCESS_LIST": "Process list",
"REFRESH": "Refresh",
"REMOVE": "Remove",
+ "APPLY": "Apply",
+ "RELOAD_FROM_DISK": "Reload from disk",
"SETTINGS": "Settings",
"SHOW": "Show",
+ "TARGET_PATH": "Target path",
"TERMINATE_PROCESS": "Terminate process",
+ "VERSION": "Version",
"WIDTH": "Width"
}
diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs
index 26a1970c..192cbc06 100644
--- a/dash-frontend/src/frontend.rs
+++ b/dash-frontend/src/frontend.rs
@@ -28,7 +28,7 @@ use crate::{
assets,
tab::{Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings},
util::{
- popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
+ popup_manager::{MountPopupOnceParams, PopupManager, PopupManagerParams},
toast_manager::ToastManager,
},
views,
@@ -101,7 +101,7 @@ pub enum FrontendTask {
SetTab(TabType),
RefreshClock,
RefreshBackground,
- MountPopup(MountPopupParams),
+ MountPopupOnce(MountPopupOnceParams),
RefreshPopupManager,
ShowAudioSettings,
UpdateAudioSettingsView,
@@ -279,10 +279,8 @@ impl Frontend {
}
fn update_time(&mut self, data: &mut T) -> anyhow::Result<()> {
- let mut c = self.layout.start_common();
- let mut common = c.common();
-
{
+ let mut common = self.layout.common();
let mut label = common
.state
.widgets
@@ -303,27 +301,20 @@ impl Frontend {
label.set_text(&mut common, Translation::from_raw_text(&text));
}
- c.finish()?;
Ok(())
}
- fn mount_popup(&mut self, params: MountPopupParams, data: &mut T) -> anyhow::Result<()> {
+ fn mount_popup_once(&mut self, params: MountPopupOnceParams, data: &mut T) -> anyhow::Result<()> {
let config = self.interface.general_config(data);
- self.popup_manager.mount_popup(
- self.globals.clone(),
- &mut self.layout,
- self.tasks.clone(),
- params,
- config,
- )?;
+ self
+ .popup_manager
+ .mount_popup_once(&self.globals, &mut self.layout, &self.tasks, params, config)?;
Ok(())
}
fn refresh_popup_manager(&mut self) -> anyhow::Result<()> {
- let mut c = self.layout.start_common();
- self.popup_manager.refresh(c.common().alterables);
- c.finish()?;
+ self.popup_manager.refresh(&mut self.layout.alterables);
Ok(())
}
@@ -356,7 +347,7 @@ impl Frontend {
FrontendTask::SetTab(tab_type) => self.set_tab(params.data, tab_type)?,
FrontendTask::RefreshClock => self.update_time(params.data)?,
FrontendTask::RefreshBackground => self.update_background(params.data)?,
- FrontendTask::MountPopup(popup_params) => self.mount_popup(popup_params, params.data)?,
+ FrontendTask::MountPopupOnce(popup_params) => self.mount_popup_once(popup_params, params.data)?,
FrontendTask::RefreshPopupManager => self.refresh_popup_manager()?,
FrontendTask::ShowAudioSettings => self.action_show_audio_settings()?,
FrontendTask::UpdateAudioSettingsView => self.action_update_audio_settings()?,
@@ -369,8 +360,7 @@ impl Frontend {
}
fn set_tab_title(&mut self, translation: &str, icon: &str) -> anyhow::Result<()> {
- let mut c = self.layout.start_common();
- let mut common = c.common();
+ let mut common = self.layout.common();
{
let mut label = common
@@ -386,12 +376,11 @@ impl Frontend {
.widgets
.cast_as::(self.widgets.id_sprite_titlebar_icon)?;
sprite.set_content(
- &mut common,
+ common.alterables,
Some(CustomGlyphData::from_assets(&self.globals, AssetPath::BuiltIn(icon))?),
);
}
- c.finish()?;
Ok(())
}
diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs
index 7e17ff39..cf093f84 100644
--- a/dash-frontend/src/tab/apps.rs
+++ b/dash-frontend/src/tab/apps.rs
@@ -9,7 +9,6 @@ use wgui::{
assets::AssetPath,
components::button::{ButtonClickCallback, ComponentButton},
globals::WguiGlobals,
- i18n::Translation,
layout::{WidgetID, WidgetPair},
parser::{Fetchable, ParseDocumentParams, ParserState},
task::Tasks,
@@ -17,18 +16,17 @@ use wgui::{
use wlx_common::desktop_finder::DesktopEntry;
use crate::{
- frontend::{Frontend, FrontendTask, FrontendTasks},
+ frontend::{Frontend, FrontendTasks},
tab::{Tab, TabType},
- util::popup_manager::{MountPopupParams, PopupHandle},
- views::{self, app_launcher},
+ util::popup_manager::PopupHolder,
+ views::{self},
};
-enum Task {
- CloseLauncher,
-}
+#[derive(Clone)]
+enum Task {}
struct State {
- view_launcher: Option<(PopupHandle, views::app_launcher::View)>,
+ view_launcher: PopupHolder,
}
pub struct TabApps {
@@ -47,21 +45,17 @@ impl Tab for TabApps {
}
fn update(&mut self, frontend: &mut Frontend, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
- let mut state = self.state.borrow_mut();
+ let state = self.state.borrow_mut();
for task in self.tasks.drain() {
- match task {
- Task::CloseLauncher => state.view_launcher = None,
- }
+ match task {}
}
- self
- .app_list
- .tick(frontend, &self.state, &self.tasks, &mut self.parser_state)?;
+ self.app_list.tick(frontend, &self.state, &mut self.parser_state)?;
- if let Some((_, launcher)) = &mut state.view_launcher {
- launcher.update(&mut frontend.interface, data)?;
- }
+ state
+ .view_launcher
+ .with_view_res(|view| view.update(&mut frontend.interface, data))?;
Ok(())
}
}
@@ -79,40 +73,14 @@ fn on_app_click(
globals: WguiGlobals,
entry: DesktopEntry,
state: Rc>,
- tasks: Tasks,
) -> ButtonClickCallback {
Rc::new(move |_common, _evt| {
- frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
- title: Translation::from_raw_text(&entry.app_name),
- on_content: {
- // this is awful
- let state = state.clone();
- let entry = entry.clone();
- let globals = globals.clone();
- let frontend_tasks = frontend_tasks.clone();
- let tasks = tasks.clone();
-
- Rc::new(move |data| {
- let on_launched = {
- let tasks = tasks.clone();
- Box::new(move || tasks.push(Task::CloseLauncher))
- };
-
- let view = app_launcher::View::new(app_launcher::Params {
- entry: entry.clone(),
- globals: &globals,
- layout: data.layout,
- parent_id: data.id_content,
- frontend_tasks: &frontend_tasks,
- config: data.config,
- on_launched,
- })?;
-
- state.borrow_mut().view_launcher = Some((data.handle, view));
- Ok(())
- })
- },
- }));
+ views::app_launcher::mount_popup(
+ frontend_tasks.clone(),
+ globals.clone(),
+ entry.clone(),
+ state.borrow_mut().view_launcher.clone(),
+ );
Ok(())
})
}
@@ -129,7 +97,9 @@ impl TabApps {
pub fn new(frontend: &mut Frontend, parent_id: WidgetID, data: &mut T) -> anyhow::Result {
let globals = frontend.layout.state.globals.clone();
let tasks = Tasks::new();
- let state = Rc::new(RefCell::new(State { view_launcher: None }));
+ let state = Rc::new(RefCell::new(State {
+ view_launcher: Default::default(),
+ }));
let parser_state = wgui::parser::parse_from_assets(&doc_params(globals.clone()), &mut frontend.layout, parent_id)?;
let app_list_parent = parser_state.fetch_widget(&frontend.layout.state, "app_list_parent")?;
@@ -334,7 +304,6 @@ impl AppList {
&mut self,
frontend: &mut Frontend,
state: &Rc>,
- tasks: &Tasks,
parser_state: &mut ParserState,
) -> anyhow::Result<()> {
// load 4 entries for a single frame at most
@@ -348,7 +317,6 @@ impl AppList {
globals.clone(),
entry.clone(),
state.clone(),
- tasks.clone(),
));
} else {
break;
diff --git a/dash-frontend/src/tab/games.rs b/dash-frontend/src/tab/games.rs
index fcdc3cad..514b7c3e 100644
--- a/dash-frontend/src/tab/games.rs
+++ b/dash-frontend/src/tab/games.rs
@@ -10,7 +10,7 @@ use crate::{
frontend::Frontend,
tab::{Tab, TabType},
util::steam_utils::SteamUtils,
- views::{game_list, running_games_list},
+ views::{ViewTrait, ViewUpdateParams, game_list, running_games_list},
};
pub struct TabGames {
@@ -19,7 +19,6 @@ pub struct TabGames {
view_game_list: game_list::View,
view_running_games_list: running_games_list::View,
- steam_utils: SteamUtils,
marker: PhantomData,
}
@@ -28,10 +27,20 @@ impl Tab for TabGames {
TabType::Games
}
- fn update(&mut self, frontend: &mut Frontend, time_ms: u32, _data: &mut T) -> anyhow::Result<()> {
- self
- .view_game_list
- .update(&mut frontend.layout, &mut self.steam_utils, &frontend.executor)?;
+ fn update(&mut self, frontend: &mut Frontend, time_ms: u32, data: &mut T) -> anyhow::Result<()> {
+ let mut config_change_kind = None;
+
+ self.view_game_list.update(&mut ViewUpdateParams {
+ layout: &mut frontend.layout,
+ executor: &frontend.executor,
+ general_config: frontend.interface.general_config(data),
+ config_change_kind: &mut config_change_kind,
+ })?;
+
+ if let Some(kind) = config_change_kind {
+ frontend.interface.config_changed(data, kind);
+ }
+
self.view_running_games_list.update(&mut frontend.layout, time_ms)?;
Ok(())
}
@@ -54,16 +63,17 @@ impl TabGames {
let game_list_parent = state.get_widget_id("game_list_parent")?;
let id_running_games_list_parent = state.get_widget_id("running_games_list_parent")?;
+ let mut steam_utils = SteamUtils::new()?;
+
let view_game_list = game_list::View::new(game_list::Params {
executor: frontend.executor.clone(),
frontend_tasks: frontend.tasks.clone(),
globals: globals.clone(),
layout: &mut frontend.layout,
parent_id: game_list_parent,
+ steam_utils: &steam_utils,
})?;
- let mut steam_utils = SteamUtils::new()?;
-
let view_running_games_list = running_games_list::View::new(running_games_list::Params {
globals: globals.clone(),
layout: &mut frontend.layout,
@@ -77,7 +87,6 @@ impl TabGames {
view_game_list,
view_running_games_list,
marker: PhantomData,
- steam_utils,
})
}
}
diff --git a/dash-frontend/src/tab/home.rs b/dash-frontend/src/tab/home.rs
index 57e967cd..bb541ee1 100644
--- a/dash-frontend/src/tab/home.rs
+++ b/dash-frontend/src/tab/home.rs
@@ -59,9 +59,12 @@ impl TabHome {
parent_id,
)?;
- let mut c = frontend.layout.start_common();
- let widget_label = state.fetch_widget(&c.layout.state, "label_hello")?.widget;
- configure_label_hello(&mut c.common(), widget_label, frontend.interface.general_config(data));
+ let widget_label = state.fetch_widget(&frontend.layout.state, "label_hello")?.widget;
+ configure_label_hello(
+ &mut frontend.layout.common(),
+ widget_label,
+ frontend.interface.general_config(data),
+ );
let btn_apps = state.fetch_component_as::("btn_apps")?;
let btn_games = state.fetch_component_as::("btn_games")?;
diff --git a/dash-frontend/src/tab/monado.rs b/dash-frontend/src/tab/monado.rs
index 8efe58de..a9c5332e 100644
--- a/dash-frontend/src/tab/monado.rs
+++ b/dash-frontend/src/tab/monado.rs
@@ -18,7 +18,7 @@ use wgui::{
};
use wlx_common::{
config::GeneralConfig,
- dash_interface::{self, MonadoDumpSessionFrame},
+ dash_interface::{self, ConfigChangeKind, MonadoDumpSessionFrame},
};
use crate::{
@@ -175,7 +175,9 @@ impl Tab for TabMonado {
Task::GeneralSettingsChromaUpdate => {
if let Subtab::GeneralSettings(tab) = &mut self.subtab {
tab.chroma_update(frontend.interface.general_config(data));
- frontend.interface.config_changed(data);
+ frontend
+ .interface
+ .config_changed(data, ConfigChangeKind::EnvironmentBlend);
}
}
Task::SetBrightness(brightness) => self.set_brightness(frontend, data, brightness),
@@ -281,9 +283,7 @@ impl SubtabGeneralSettings {
// get brightness
let slider_brightness = state.fetch_component_as::("slider_brightness")?;
if let Some(brightness) = frontend.interface.monado_brightness_get(data) {
- let mut c = frontend.layout.start_common();
- slider_brightness.set_value(&mut c.common(), brightness * 100.0);
- c.finish()?;
+ slider_brightness.set_value_primary(&mut frontend.layout.common(), brightness * 100.0);
slider_brightness.on_value_changed({
let tasks = tasks.clone();
@@ -304,16 +304,15 @@ impl SubtabGeneralSettings {
let slider_keying_value_range = state.fetch_component_as::("slider_keying_value_range")?;
{
- let mut lc = frontend.layout.start_common();
- let mut common = lc.common();
+ let mut common = frontend.layout.common();
// set initial values
let (rgb, range_h, range_s, range_v) = config.chroma_key_params.get_rgb_and_hsv_ranges();
- slider_keying_curve.set_value(&mut common, config.chroma_key_params.curve * SLIDER_MULTIPLIER);
- slider_keying_despill.set_value(&mut common, config.chroma_key_params.despill * SLIDER_MULTIPLIER);
- slider_keying_hue_range.set_value(&mut common, range_h * SLIDER_MULTIPLIER);
- slider_keying_saturation_range.set_value(&mut common, range_s * SLIDER_MULTIPLIER);
- slider_keying_value_range.set_value(&mut common, range_v * SLIDER_MULTIPLIER);
+ slider_keying_curve.set_value_primary(&mut common, config.chroma_key_params.curve * SLIDER_MULTIPLIER);
+ slider_keying_despill.set_value_primary(&mut common, config.chroma_key_params.despill * SLIDER_MULTIPLIER);
+ slider_keying_hue_range.set_value_primary(&mut common, range_h * SLIDER_MULTIPLIER);
+ slider_keying_saturation_range.set_value_primary(&mut common, range_s * SLIDER_MULTIPLIER);
+ slider_keying_value_range.set_value_primary(&mut common, range_v * SLIDER_MULTIPLIER);
cs_keying.set_color(&mut common, rgb);
// prepare callbacks
@@ -340,8 +339,6 @@ impl SubtabGeneralSettings {
slider_keying_saturation_range.on_value_changed(get_slider_callback(&tasks));
slider_keying_value_range.on_value_changed(get_slider_callback(&tasks));
cs_keying.on_changed(get_color_selector_callback(&tasks));
-
- lc.finish()?;
}
Ok(Self {
@@ -356,11 +353,11 @@ impl SubtabGeneralSettings {
}
fn chroma_update(&mut self, config: &mut GeneralConfig) {
- let val_curve = self.slider_keying_curve.get_value();
- let val_despill = self.slider_keying_despill.get_value();
- let val_range_h = self.slider_keying_hue_range.get_value();
- let val_range_s = self.slider_keying_saturation_range.get_value();
- let val_range_v = self.slider_keying_value_range.get_value();
+ let val_curve = self.slider_keying_curve.get_value_primary();
+ let val_despill = self.slider_keying_despill.get_value_primary();
+ let val_range_h = self.slider_keying_hue_range.get_value_primary();
+ let val_range_s = self.slider_keying_saturation_range.get_value_primary();
+ let val_range_v = self.slider_keying_value_range.get_value_primary();
let val_rgb = self.cs_keying.get_color();
config.chroma_key_params.despill = val_despill / SLIDER_MULTIPLIER;
diff --git a/dash-frontend/src/tab/settings/mod.rs b/dash-frontend/src/tab/settings/mod.rs
index 55eb02a9..32ec394a 100644
--- a/dash-frontend/src/tab/settings/mod.rs
+++ b/dash-frontend/src/tab/settings/mod.rs
@@ -5,7 +5,7 @@ use wgui::{
assets::AssetPath,
components::tabs::ComponentTabs,
drawing,
- event::{CallbackDataCommon, EventAlterables},
+ event::StyleSetRequest,
globals::WguiGlobals,
i18n::Translation,
layout::{Layout, WidgetID},
@@ -20,11 +20,16 @@ use wgui::{
},
windowing::context_menu::{self, Blueprint, ContextMenu, TickResult},
};
-use wlx_common::{config::GeneralConfig, config_io::ConfigRoot, dash_interface::RecenterMode};
+use wlx_common::{
+ config::GeneralConfig,
+ config_io::ConfigRoot,
+ dash_interface::{ConfigChangeKind, RecenterMode},
+};
use crate::{
- frontend::{Frontend, FrontendTask},
+ frontend::{Frontend, FrontendTask, FrontendTasks},
tab::{Tab, TabType, settings::macros::MacroParams},
+ views::ViewUpdateParams,
};
mod macros;
@@ -33,6 +38,7 @@ mod tab_controls;
mod tab_features;
mod tab_look_and_feel;
mod tab_misc;
+mod tab_skybox;
mod tab_troubleshooting;
#[derive(Clone)]
@@ -43,6 +49,7 @@ enum TabNameEnum {
Misc,
AutostartApps,
Troubleshooting,
+ Skybox,
}
impl TabNameEnum {
@@ -54,6 +61,7 @@ impl TabNameEnum {
"misc" => Some(TabNameEnum::Misc),
"autostart_apps" => Some(TabNameEnum::AutostartApps),
"troubleshooting" => Some(TabNameEnum::Troubleshooting),
+ "skybox" => Some(TabNameEnum::Skybox),
_ => None,
}
}
@@ -75,14 +83,29 @@ enum Task {
SetTab(TabNameEnum),
}
+struct SettingsMountParams<'a> {
+ mp: &'a mut MacroParams<'a>,
+ frontend_tasks: &'a FrontendTasks,
+ id_parent: WidgetID,
+}
+
+trait SettingsTab {
+ fn update(&mut self, _par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ Ok(())
+ }
+}
+
pub struct TabSettings {
pub state: ParserState,
app_button_ids: Vec>,
context_menu: ContextMenu,
+ current_tab: Option>,
+
tasks: Tasks,
marker: PhantomData,
+ frontend_tasks: FrontendTasks,
}
impl Tab for TabSettings {
@@ -91,7 +114,22 @@ impl Tab for TabSettings {
}
fn update(&mut self, frontend: &mut Frontend, _time_ms: u32, data: &mut T) -> anyhow::Result<()> {
- let mut changed = false;
+ if let Some(tab) = &mut self.current_tab {
+ let mut config_change_kind = None;
+
+ tab.update(&mut ViewUpdateParams {
+ layout: &mut frontend.layout,
+ executor: &frontend.executor,
+ general_config: frontend.interface.general_config(data),
+ config_change_kind: &mut config_change_kind,
+ })?;
+
+ if let Some(kind) = config_change_kind {
+ frontend.interface.config_changed(data, kind);
+ }
+ }
+
+ let mut changed = None;
for task in self.tasks.drain() {
match task {
Task::SetTab(tab) => {
@@ -104,7 +142,7 @@ impl Tab for TabSettings {
}
let config = frontend.interface.general_config(data);
*setting.mut_bool(config) = n;
- changed = true;
+ changed = Some(setting.change_kind());
}
Task::UpdateFloat(setting, n) => {
self.tasks.push(Task::SettingUpdated(setting));
@@ -113,7 +151,7 @@ impl Tab for TabSettings {
}
let config = frontend.interface.general_config(data);
*setting.mut_f32(config) = n;
- changed = true;
+ changed = Some(setting.change_kind());
}
Task::UpdateInt(setting, n) => {
self.tasks.push(Task::SettingUpdated(setting));
@@ -122,7 +160,7 @@ impl Tab for TabSettings {
}
let config = frontend.interface.general_config(data);
*setting.mut_i32(config) = n;
- changed = true;
+ changed = Some(setting.change_kind());
}
Task::ClearPipewireTokens => {
let _ = std::fs::remove_file(ConfigRoot::Generic.get_conf_d_path().join("pw_tokens.yaml"))
@@ -161,7 +199,7 @@ impl Tab for TabSettings {
let config = frontend.interface.general_config(data);
config.autostart_apps.remove(idx);
frontend.layout.remove_widget(widget);
- changed = true;
+ changed = Some(ConfigChangeKind::OverlayConfig);
}
}
Task::SettingUpdated(setting) => match setting {
@@ -179,15 +217,10 @@ impl Tab for TabSettings {
let mut s = name.splitn(5, ';');
(s.next(), s.next(), s.next(), s.next(), s.next())
} {
+ let mut common = frontend.layout.common();
let mut label = self
.state
- .fetch_widget_as::(&frontend.layout.state, &format!("{id}_value"))?;
-
- let mut alterables = EventAlterables::default();
- let mut common = CallbackDataCommon {
- alterables: &mut alterables,
- state: &frontend.layout.state,
- };
+ .fetch_widget_as::(&common.state, &format!("{id}_value"))?;
let translation = Translation {
text: text.into(),
@@ -199,12 +232,12 @@ impl Tab for TabSettings {
let setting = SettingType::from_str(setting).expect("Invalid Enum string");
let config = frontend.interface.general_config(data);
setting.set_enum(config, value);
- changed = true;
+ changed = Some(ConfigChangeKind::OverlayConfig);
}
// Notify overlays of the change
- if changed {
- frontend.interface.config_changed(data);
+ if let Some(changed) = changed {
+ frontend.interface.config_changed(data, changed);
}
Ok(())
@@ -231,7 +264,6 @@ enum SettingType {
InvertScrollDirectionY,
KeyboardMiddleClick,
KeyboardSoundEnabled,
- KeyboardSwipeToTypeEnabled,
Language,
LeftHandedMouse,
LongPressDuration,
@@ -258,6 +290,13 @@ enum SettingType {
}
impl SettingType {
+ pub fn change_kind(self) -> ConfigChangeKind {
+ match self {
+ Self::UseSkybox | Self::UsePassthrough => ConfigChangeKind::EnvironmentBlend,
+ _ => ConfigChangeKind::OverlayConfig,
+ }
+ }
+
pub fn mut_bool(self, config: &mut GeneralConfig) -> &mut bool {
match self {
Self::InvertScrollDirectionX => &mut config.invert_scroll_direction_x,
@@ -284,7 +323,6 @@ impl SettingType {
Self::HideUsername => &mut config.hide_username,
Self::OpaqueBackground => &mut config.opaque_background,
Self::XwaylandByDefault => &mut config.xwayland_by_default,
- Self::KeyboardSwipeToTypeEnabled => &mut config.keyboard_swipe_to_type_enabled,
_ => panic!("Requested bool for non-bool SettingType"),
}
}
@@ -383,7 +421,6 @@ impl SettingType {
Self::InvertScrollDirectionY => Ok("APP_SETTINGS.INVERT_SCROLL_DIRECTION_Y"),
Self::KeyboardMiddleClick => Ok("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK"),
Self::KeyboardSoundEnabled => Ok("APP_SETTINGS.KEYBOARD_SOUND_ENABLED"),
- Self::KeyboardSwipeToTypeEnabled => Ok("APP_SETTINGS.KEYBOARD_SWIPE_TO_TYPE_ENABLED"),
Self::Language => Ok("APP_SETTINGS.LANGUAGE"),
Self::LeftHandedMouse => Ok("APP_SETTINGS.LEFT_HANDED_MOUSE"),
Self::LongPressDuration => Ok("APP_SETTINGS.LONG_PRESS_DURATION"),
@@ -420,7 +457,6 @@ impl SettingType {
Self::GridOpacity => Some("APP_SETTINGS.GRID_OPACITY_HELP"),
Self::HandsfreePointer => Some("APP_SETTINGS.HANDSFREE_POINTER_HELP"),
Self::KeyboardMiddleClick => Some("APP_SETTINGS.KEYBOARD_MIDDLE_CLICK_HELP"),
- Self::KeyboardSwipeToTypeEnabled => Some("APP_SETTINGS.KEYBOARD_SWIPE_TO_TYPE_ENABLED_HELP"),
Self::LeftHandedMouse => Some("APP_SETTINGS.LEFT_HANDED_MOUSE_HELP"),
Self::ScreenRenderDown => Some("APP_SETTINGS.SCREEN_RENDER_DOWN_HELP"),
Self::UprightScreenFix => Some("APP_SETTINGS.UPRIGHT_SCREEN_FIX_HELP"),
@@ -440,7 +476,6 @@ impl SettingType {
| Self::UiGradientIntensity
| Self::UprightScreenFix
| Self::DoubleCursorFix
- | Self::ScreenRenderDown
| Self::Language
| Self::CaptureMethod
)
@@ -504,6 +539,7 @@ impl TabSettings {
let root = self.state.get_widget_id("settings_root")?;
frontend.layout.remove_children(root);
let globals = frontend.layout.state.globals.clone();
+ self.current_tab = None;
let mut mp = MacroParams {
layout: &mut frontend.layout,
@@ -514,31 +550,43 @@ impl TabSettings {
idx: 9001,
};
+ let settings_mount_params = SettingsMountParams {
+ mp: &mut mp,
+ id_parent: root,
+ frontend_tasks: &self.frontend_tasks,
+ };
+
match name {
TabNameEnum::LookAndFeel => {
- tab_look_and_feel::mount(&mut mp, root)?;
+ self.current_tab = Some(Box::new(tab_look_and_feel::State::mount(settings_mount_params)?));
}
TabNameEnum::Features => {
- tab_features::mount(&mut mp, root)?;
+ self.current_tab = Some(Box::new(tab_features::State::mount(settings_mount_params)?));
}
TabNameEnum::Controls => {
- tab_controls::mount(&mut mp, root)?;
+ self.current_tab = Some(Box::new(tab_controls::State::mount(settings_mount_params)?));
}
TabNameEnum::Misc => {
- tab_misc::mount(&mut mp, root)?;
+ self.current_tab = Some(Box::new(tab_misc::State::mount(settings_mount_params)?));
}
TabNameEnum::AutostartApps => {
- tab_autostart_apps::mount(&mut mp, root, &mut self.app_button_ids)?;
+ self.current_tab = Some(Box::new(tab_autostart_apps::State::mount(
+ settings_mount_params,
+ &mut self.app_button_ids,
+ )?));
}
TabNameEnum::Troubleshooting => {
- tab_troubleshooting::mount(&mut mp, root)?;
+ self.current_tab = Some(Box::new(tab_troubleshooting::State::mount(settings_mount_params)?));
+ }
+ TabNameEnum::Skybox => {
+ self.current_tab = Some(Box::new(tab_skybox::State::mount(settings_mount_params)?));
}
}
Ok(())
}
- pub fn new(frontend: &mut Frontend, parent_id: WidgetID, _data: &mut T) -> anyhow::Result {
+ pub fn new(frontend: &mut Frontend, parent_id: WidgetID, data: &mut T) -> anyhow::Result {
let doc_params = ParseDocumentParams {
globals: frontend.layout.state.globals.clone(),
path: AssetPath::BuiltIn("gui/tab/settings.xml"),
@@ -548,6 +596,16 @@ impl TabSettings {
let parser_state = wgui::parser::parse_from_assets(&doc_params, &mut frontend.layout, parent_id)?;
let tasks = Tasks::default();
let tabs = parser_state.fetch_component_as::("tabs")?;
+
+ if !frontend.interface.get_feats(data).openxr {
+ let skybox_btn = tabs.get_tab_button("skybox").unwrap();
+ frontend
+ .layout
+ .common()
+ .alterables
+ .set_style(skybox_btn.get_rect(), StyleSetRequest::Display(taffy::Display::None));
+ }
+
tabs.on_select({
let tasks = tasks.clone();
Rc::new(move |_common, evt| {
@@ -566,6 +624,8 @@ impl TabSettings {
state: parser_state,
marker: PhantomData,
context_menu: ContextMenu::default(),
+ current_tab: None,
+ frontend_tasks: frontend.tasks.clone(),
})
}
}
diff --git a/dash-frontend/src/tab/settings/tab_autostart_apps.rs b/dash-frontend/src/tab/settings/tab_autostart_apps.rs
index 6483fc94..76986f88 100644
--- a/dash-frontend/src/tab/settings/tab_autostart_apps.rs
+++ b/dash-frontend/src/tab/settings/tab_autostart_apps.rs
@@ -1,19 +1,33 @@
use std::rc::Rc;
-use crate::tab::settings::macros::{MacroParams, options_autostart_app, options_category};
-use wgui::layout::WidgetID;
+use crate::tab::settings::{
+ SettingsMountParams, SettingsTab,
+ macros::{options_autostart_app, options_category},
+};
-pub fn mount(mp: &mut MacroParams, parent: WidgetID, app_button_ids: &mut Vec>) -> anyhow::Result<()> {
- *app_button_ids = Vec::new();
+pub struct State {}
- if !mp.config.autostart_apps.is_empty() {
- let c = options_category(mp, parent, "APP_SETTINGS.AUTOSTART_APPS", "dashboard/apps.svg")?;
+impl SettingsTab for State {}
- // todo: prevent clone
- let autostart_apps = mp.config.autostart_apps.clone();
- for app in autostart_apps {
- options_autostart_app(mp, c, &app.name, app_button_ids)?;
+impl State {
+ pub fn mount(par: SettingsMountParams, app_button_ids: &mut Vec>) -> anyhow::Result {
+ *app_button_ids = Vec::new();
+
+ if !par.mp.config.autostart_apps.is_empty() {
+ let c = options_category(
+ par.mp,
+ par.id_parent,
+ "APP_SETTINGS.AUTOSTART_APPS",
+ "dashboard/apps.svg",
+ )?;
+
+ // todo: prevent clone
+ let autostart_apps = par.mp.config.autostart_apps.clone();
+ for app in autostart_apps {
+ options_autostart_app(par.mp, c, &app.name, app_button_ids)?;
+ }
}
+
+ Ok(State {})
}
- Ok(())
}
diff --git a/dash-frontend/src/tab/settings/tab_controls.rs b/dash-frontend/src/tab/settings/tab_controls.rs
index 27b1f10b..266df0c9 100644
--- a/dash-frontend/src/tab/settings/tab_controls.rs
+++ b/dash-frontend/src/tab/settings/tab_controls.rs
@@ -1,23 +1,33 @@
use crate::tab::settings::{
- SettingType,
- macros::{MacroParams, options_category, options_checkbox, options_dropdown, options_slider_f32, options_slider_i32},
+ SettingType, SettingsMountParams, SettingsTab,
+ macros::{options_category, options_checkbox, options_dropdown, options_slider_f32, options_slider_i32},
};
-use wgui::layout::WidgetID;
-pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
- let c = options_category(mp, parent, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?;
- options_dropdown::(mp, c, &SettingType::KeyboardMiddleClick)?;
- options_dropdown::(mp, c, &SettingType::HandsfreePointer)?;
- options_checkbox(mp, c, SettingType::FocusFollowsMouseMode)?;
- options_checkbox(mp, c, SettingType::LeftHandedMouse)?;
- options_checkbox(mp, c, SettingType::AllowSliding)?;
- options_checkbox(mp, c, SettingType::InvertScrollDirectionX)?;
- options_checkbox(mp, c, SettingType::InvertScrollDirectionY)?;
- options_slider_f32(mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1)?;
- options_slider_f32(mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1)?;
- options_slider_f32(mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1)?;
- options_slider_f32(mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1)?;
- options_slider_f32(mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1)?;
- options_slider_i32(mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50)?;
- Ok(())
+pub struct State {}
+
+impl SettingsTab for State {}
+
+impl State {
+ pub fn mount(par: SettingsMountParams) -> anyhow::Result {
+ let c = options_category(
+ par.mp,
+ par.id_parent,
+ "APP_SETTINGS.CONTROLS",
+ "dashboard/controller.svg",
+ )?;
+ options_dropdown::(par.mp, c, &SettingType::KeyboardMiddleClick)?;
+ options_dropdown::(par.mp, c, &SettingType::HandsfreePointer)?;
+ options_checkbox(par.mp, c, SettingType::FocusFollowsMouseMode)?;
+ options_checkbox(par.mp, c, SettingType::LeftHandedMouse)?;
+ options_checkbox(par.mp, c, SettingType::AllowSliding)?;
+ options_checkbox(par.mp, c, SettingType::InvertScrollDirectionX)?;
+ options_checkbox(par.mp, c, SettingType::InvertScrollDirectionY)?;
+ options_slider_f32(par.mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1)?;
+ options_slider_f32(par.mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1)?;
+ options_slider_f32(par.mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1)?;
+ options_slider_f32(par.mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1)?;
+ options_slider_f32(par.mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1)?;
+ options_slider_i32(par.mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50)?;
+ Ok(State {})
+ }
}
diff --git a/dash-frontend/src/tab/settings/tab_features.rs b/dash-frontend/src/tab/settings/tab_features.rs
index 264a40fd..992bf06c 100644
--- a/dash-frontend/src/tab/settings/tab_features.rs
+++ b/dash-frontend/src/tab/settings/tab_features.rs
@@ -1,20 +1,24 @@
use crate::tab::settings::{
- SettingType,
- macros::{MacroParams, options_category, options_checkbox, options_slider_f32},
+ SettingType, SettingsMountParams, SettingsTab,
+ macros::{options_category, options_checkbox, options_slider_f32},
};
-use wgui::layout::WidgetID;
-pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
- let c = options_category(mp, parent, "APP_SETTINGS.FEATURES", "dashboard/options.svg")?;
- options_checkbox(mp, c, SettingType::NotificationsEnabled)?;
- options_checkbox(mp, c, SettingType::NotificationsSoundEnabled)?;
- options_checkbox(mp, c, SettingType::KeyboardSoundEnabled)?;
- options_checkbox(mp, c, SettingType::KeyboardSwipeToTypeEnabled)?;
- options_checkbox(mp, c, SettingType::SpaceDragUnlocked)?;
- options_checkbox(mp, c, SettingType::SpaceRotateUnlocked)?;
- options_slider_f32(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5)?;
- options_checkbox(mp, c, SettingType::BlockGameInput)?;
- options_checkbox(mp, c, SettingType::BlockGameInputIgnoreWatch)?;
- options_checkbox(mp, c, SettingType::BlockPosesOnKbdInteraction)?;
- Ok(())
+pub struct State {}
+
+impl SettingsTab for State {}
+
+impl State {
+ pub fn mount(par: SettingsMountParams) -> anyhow::Result {
+ let c = options_category(par.mp, par.id_parent, "APP_SETTINGS.FEATURES", "dashboard/options.svg")?;
+ options_checkbox(par.mp, c, SettingType::NotificationsEnabled)?;
+ options_checkbox(par.mp, c, SettingType::NotificationsSoundEnabled)?;
+ options_checkbox(par.mp, c, SettingType::KeyboardSoundEnabled)?;
+ options_checkbox(par.mp, c, SettingType::SpaceDragUnlocked)?;
+ options_checkbox(par.mp, c, SettingType::SpaceRotateUnlocked)?;
+ options_slider_f32(par.mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5)?;
+ options_checkbox(par.mp, c, SettingType::BlockGameInput)?;
+ options_checkbox(par.mp, c, SettingType::BlockGameInputIgnoreWatch)?;
+ options_checkbox(par.mp, c, SettingType::BlockPosesOnKbdInteraction)?;
+ Ok(State {})
+ }
}
diff --git a/dash-frontend/src/tab/settings/tab_look_and_feel.rs b/dash-frontend/src/tab/settings/tab_look_and_feel.rs
index 6fe40420..c632e667 100644
--- a/dash-frontend/src/tab/settings/tab_look_and_feel.rs
+++ b/dash-frontend/src/tab/settings/tab_look_and_feel.rs
@@ -1,22 +1,29 @@
use crate::tab::settings::{
- SettingType,
- macros::{MacroParams, options_category, options_checkbox, options_dropdown, options_slider_f32},
+ SettingType, SettingsMountParams, SettingsTab,
+ macros::{options_category, options_checkbox, options_dropdown, options_slider_f32},
};
-use wgui::layout::WidgetID;
-pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
- let c = options_category(mp, parent, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/palette.svg")?;
- options_dropdown::(mp, c, &SettingType::Language)?;
- options_checkbox(mp, c, SettingType::OpaqueBackground)?;
- options_checkbox(mp, c, SettingType::HideUsername)?;
- options_checkbox(mp, c, SettingType::HideGrabHelp)?;
- options_slider_f32(mp, c, SettingType::UiAnimationSpeed, 0.5, 5.0, 0.1)?; // min, max, step
- options_slider_f32(mp, c, SettingType::UiGradientIntensity, 0.0, 1.0, 0.05)?; // min, max, step
- options_slider_f32(mp, c, SettingType::UiRoundMultiplier, 0.5, 5.0, 0.1)?;
- options_checkbox(mp, c, SettingType::SetsOnWatch)?;
- options_checkbox(mp, c, SettingType::UseSkybox)?;
- options_slider_f32(mp, c, SettingType::GridOpacity, 0.0, 1.0, 0.05)?; // min, max, step
- options_checkbox(mp, c, SettingType::UsePassthrough)?;
- options_checkbox(mp, c, SettingType::Clock12h)?;
- Ok(())
+pub struct State {}
+
+impl SettingsTab for State {}
+
+impl State {
+ pub fn mount(par: SettingsMountParams) -> anyhow::Result {
+ let c = options_category(
+ par.mp,
+ par.id_parent,
+ "APP_SETTINGS.LOOK_AND_FEEL",
+ "dashboard/palette.svg",
+ )?;
+ options_dropdown::(par.mp, c, &SettingType::Language)?;
+ options_checkbox(par.mp, c, SettingType::OpaqueBackground)?;
+ options_checkbox(par.mp, c, SettingType::HideUsername)?;
+ options_checkbox(par.mp, c, SettingType::HideGrabHelp)?;
+ options_slider_f32(par.mp, c, SettingType::UiAnimationSpeed, 0.5, 5.0, 0.1)?; // min, max, step
+ options_slider_f32(par.mp, c, SettingType::UiGradientIntensity, 0.0, 1.0, 0.05)?; // min, max, step
+ options_slider_f32(par.mp, c, SettingType::UiRoundMultiplier, 0.5, 5.0, 0.1)?;
+ options_checkbox(par.mp, c, SettingType::SetsOnWatch)?;
+ options_checkbox(par.mp, c, SettingType::Clock12h)?;
+ Ok(State {})
+ }
}
diff --git a/dash-frontend/src/tab/settings/tab_misc.rs b/dash-frontend/src/tab/settings/tab_misc.rs
index 9fa6f429..29b57522 100644
--- a/dash-frontend/src/tab/settings/tab_misc.rs
+++ b/dash-frontend/src/tab/settings/tab_misc.rs
@@ -1,15 +1,20 @@
use crate::tab::settings::{
- SettingType,
- macros::{MacroParams, options_category, options_checkbox, options_dropdown},
+ SettingType, SettingsMountParams, SettingsTab,
+ macros::{options_category, options_checkbox, options_dropdown},
};
-use wgui::layout::WidgetID;
-pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
- let c = options_category(mp, parent, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?;
- options_dropdown::(mp, c, &SettingType::CaptureMethod)?;
- options_checkbox(mp, c, SettingType::XwaylandByDefault)?;
- options_checkbox(mp, c, SettingType::UprightScreenFix)?;
- options_checkbox(mp, c, SettingType::DoubleCursorFix)?;
- options_checkbox(mp, c, SettingType::ScreenRenderDown)?;
- Ok(())
+pub struct State {}
+
+impl SettingsTab for State {}
+
+impl State {
+ pub fn mount(par: SettingsMountParams) -> anyhow::Result {
+ let c = options_category(par.mp, par.id_parent, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?;
+ options_dropdown::(par.mp, c, &SettingType::CaptureMethod)?;
+ options_checkbox(par.mp, c, SettingType::XwaylandByDefault)?;
+ options_checkbox(par.mp, c, SettingType::UprightScreenFix)?;
+ options_checkbox(par.mp, c, SettingType::DoubleCursorFix)?;
+ options_checkbox(par.mp, c, SettingType::ScreenRenderDown)?;
+ Ok(State {})
+ }
}
diff --git a/dash-frontend/src/tab/settings/tab_skybox.rs b/dash-frontend/src/tab/settings/tab_skybox.rs
new file mode 100644
index 00000000..66c2edf8
--- /dev/null
+++ b/dash-frontend/src/tab/settings/tab_skybox.rs
@@ -0,0 +1,70 @@
+use wgui::{assets::AssetPath, i18n::Translation, layout::Layout, task::Tasks};
+
+use crate::{
+ frontend::FrontendTasks,
+ tab::settings::{
+ SettingType, SettingsMountParams, SettingsTab,
+ macros::{options_category, options_checkbox, options_slider_f32},
+ },
+ util::{popup_manager::PopupHolder, wgui_simple},
+ views::{self, ViewUpdateParams, skymap_list},
+};
+
+#[derive(Clone)]
+enum Task {
+ ShowSkymapList,
+}
+
+pub struct State {
+ popup_skymap_list: PopupHolder,
+ tasks: Tasks,
+ frontend_tasks: FrontendTasks,
+}
+
+impl SettingsTab for State {
+ fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ self.popup_skymap_list.update(par)?;
+
+ for task in self.tasks.drain() {
+ match task {
+ Task::ShowSkymapList => self.show_skymap_list(par.layout),
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl State {
+ pub fn mount(par: SettingsMountParams) -> anyhow::Result {
+ let id_category = options_category(par.mp, par.id_parent, "APP_SETTINGS.SKYBOX", "dashboard/globe.svg")?;
+ options_checkbox(par.mp, id_category, SettingType::UseSkybox)?;
+ options_checkbox(par.mp, id_category, SettingType::UsePassthrough)?;
+ options_slider_f32(par.mp, id_category, SettingType::GridOpacity, 0.0, 1.0, 0.05)?;
+
+ let tasks = Tasks::::new();
+
+ // "Browse skymaps" button
+ wgui_simple::create_button(wgui_simple::CreateButtonParams {
+ id_parent: id_category,
+ layout: par.mp.layout,
+ content: Translation::from_translation_key("APP_SETTINGS.BROWSE_SKYMAPS"),
+ icon_builtin: AssetPath::BuiltIn("dashboard/globe.svg"),
+ on_click: tasks.get_button_click_callback(Task::ShowSkymapList),
+ })?;
+
+ Ok(Self {
+ popup_skymap_list: Default::default(),
+ frontend_tasks: par.frontend_tasks.clone(),
+ tasks,
+ })
+ }
+
+ fn show_skymap_list(&mut self, layout: &mut Layout) {
+ views::skymap_list::mount_popup(
+ self.frontend_tasks.clone(),
+ layout.state.globals.clone(),
+ self.popup_skymap_list.clone(),
+ );
+ }
+}
diff --git a/dash-frontend/src/tab/settings/tab_troubleshooting.rs b/dash-frontend/src/tab/settings/tab_troubleshooting.rs
index 9e16fd4b..8635efe5 100644
--- a/dash-frontend/src/tab/settings/tab_troubleshooting.rs
+++ b/dash-frontend/src/tab/settings/tab_troubleshooting.rs
@@ -1,45 +1,55 @@
use crate::tab::settings::{
- Task,
- macros::{MacroParams, options_category, options_danger_button},
+ SettingsMountParams, SettingsTab, Task,
+ macros::{options_category, options_danger_button},
};
-use wgui::layout::WidgetID;
-pub fn mount(mp: &mut MacroParams, parent: WidgetID) -> anyhow::Result<()> {
- let c = options_category(mp, parent, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/cpu.svg")?;
- options_danger_button(
- mp,
- c,
- "APP_SETTINGS.RESET_PLAYSPACE",
- "dashboard/recenter.svg",
- Task::ResetPlayspace,
- )?;
- options_danger_button(
- mp,
- c,
- "APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
- "dashboard/display.svg",
- Task::ClearPipewireTokens,
- )?;
- options_danger_button(
- mp,
- c,
- "APP_SETTINGS.CLEAR_SAVED_STATE",
- "dashboard/binary.svg",
- Task::ClearSavedState,
- )?;
- options_danger_button(
- mp,
- c,
- "APP_SETTINGS.DELETE_ALL_CONFIGS",
- "dashboard/circle.svg",
- Task::DeleteAllConfigs,
- )?;
- options_danger_button(
- mp,
- c,
- "APP_SETTINGS.RESTART_SOFTWARE",
- "dashboard/refresh.svg",
- Task::RestartSoftware,
- )?;
- Ok(())
+pub struct State {}
+
+impl SettingsTab for State {}
+
+impl State {
+ pub fn mount(par: SettingsMountParams) -> anyhow::Result {
+ let c = options_category(
+ par.mp,
+ par.id_parent,
+ "APP_SETTINGS.TROUBLESHOOTING",
+ "dashboard/cpu.svg",
+ )?;
+ options_danger_button(
+ par.mp,
+ c,
+ "APP_SETTINGS.RESET_PLAYSPACE",
+ "dashboard/recenter.svg",
+ Task::ResetPlayspace,
+ )?;
+ options_danger_button(
+ par.mp,
+ c,
+ "APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
+ "dashboard/display.svg",
+ Task::ClearPipewireTokens,
+ )?;
+ options_danger_button(
+ par.mp,
+ c,
+ "APP_SETTINGS.CLEAR_SAVED_STATE",
+ "dashboard/binary.svg",
+ Task::ClearSavedState,
+ )?;
+ options_danger_button(
+ par.mp,
+ c,
+ "APP_SETTINGS.DELETE_ALL_CONFIGS",
+ "dashboard/circle.svg",
+ Task::DeleteAllConfigs,
+ )?;
+ options_danger_button(
+ par.mp,
+ c,
+ "APP_SETTINGS.RESTART_SOFTWARE",
+ "dashboard/refresh.svg",
+ Task::RestartSoftware,
+ )?;
+ Ok(State {})
+ }
}
diff --git a/dash-frontend/src/util/cached_fetcher.rs b/dash-frontend/src/util/cached_fetcher.rs
index 5c7f84bc..2671a748 100644
--- a/dash-frontend/src/util/cached_fetcher.rs
+++ b/dash-frontend/src/util/cached_fetcher.rs
@@ -1,9 +1,8 @@
+use crate::util::{networking::http_client, steam_utils::AppID};
use anyhow::Context;
use serde::Deserialize;
use wlx_common::{async_executor::AsyncExecutor, cache_dir};
-use crate::util::{http_client, steam_utils::AppID};
-
pub struct CoverArt {
// can be empty in case if data couldn't be fetched (use a fallback image then)
pub compressed_image_data: Vec,
@@ -24,7 +23,7 @@ pub async fn request_image(executor: AsyncExecutor, app_id: AppID) -> anyhow::Re
app_id
);
- match http_client::get(&executor, &url).await {
+ match http_client::get_simple(&executor, &url).await {
Ok(response) => {
log::info!("Success");
cache_dir::set_data(&cache_file_path, &response.data).await?;
@@ -69,7 +68,7 @@ async fn get_app_details_json_internal(
// Fetch from Steam API
log::info!("Fetching app detail ID {}", app_id);
let url = format!("https://store.steampowered.com/api/appdetails?appids={}", app_id);
- let response = http_client::get(&executor, &url).await?;
+ let response = http_client::get_simple(&executor, &url).await?;
let res_utf8 = String::from_utf8(response.data)?;
let root = serde_json::from_str::(&res_utf8)?;
let body = root.get(&app_id).context("invalid body")?;
diff --git a/dash-frontend/src/util/mod.rs b/dash-frontend/src/util/mod.rs
index e5290607..521889d0 100644
--- a/dash-frontend/src/util/mod.rs
+++ b/dash-frontend/src/util/mod.rs
@@ -1,5 +1,5 @@
pub mod cached_fetcher;
-pub mod http_client;
+pub mod networking;
pub mod pactl_wrapper;
pub mod popup_manager;
pub mod steam_utils;
diff --git a/dash-frontend/src/util/http_client.rs b/dash-frontend/src/util/networking/http_client.rs
similarity index 70%
rename from dash-frontend/src/util/http_client.rs
rename to dash-frontend/src/util/networking/http_client.rs
index 875d509e..812620c3 100644
--- a/dash-frontend/src/util/http_client.rs
+++ b/dash-frontend/src/util/networking/http_client.rs
@@ -18,10 +18,33 @@ pub struct HttpClientResponse {
pub data: Vec,
}
-pub async fn get(executor: &AsyncExecutor, url: &str) -> anyhow::Result {
- log::info!("fetching URL \"{}\"", url);
+impl HttpClientResponse {
+ pub fn as_json(self) -> anyhow::Result
+ where
+ T: for<'a> serde::Deserialize<'a>,
+ {
+ let utf8 = str::from_utf8(&self.data)?;
+ Ok(serde_json::from_str::(utf8)?)
+ }
+}
- let url: hyper::Uri = url.try_into()?;
+pub struct ProgressFuncData {
+ pub bytes_downloaded: u64,
+ pub file_size: u64,
+}
+
+pub type ProgressFunc = Box;
+
+pub struct GetParams<'a> {
+ pub executor: &'a AsyncExecutor,
+ pub url: &'a str,
+ pub on_progress: Option,
+}
+
+pub async fn get(params: GetParams<'_>) -> anyhow::Result {
+ log::info!("fetching URL \"{}\"", params.url);
+
+ let url: hyper::Uri = params.url.try_into()?;
let req = Request::builder()
.header(
hyper::header::HOST,
@@ -30,23 +53,56 @@ pub async fn get(executor: &AsyncExecutor, url: &str) -> anyhow::Result anyhow::Result {
+ get(GetParams {
+ executor,
+ url,
+ on_progress: None,
+ })
+ .await
}
async fn fetch(
diff --git a/dash-frontend/src/util/networking/image_fetch.rs b/dash-frontend/src/util/networking/image_fetch.rs
new file mode 100644
index 00000000..75d7c892
--- /dev/null
+++ b/dash-frontend/src/util/networking/image_fetch.rs
@@ -0,0 +1,16 @@
+use std::rc::Rc;
+
+use wgui::{globals::WguiGlobals, renderer_vk::text::custom_glyph::CustomGlyphData};
+use wlx_common::async_executor::AsyncExecutor;
+
+use crate::util::networking::http_client;
+
+pub async fn fetch_to_glyph_data(
+ globals: &WguiGlobals,
+ executor: &AsyncExecutor,
+ url: &str,
+) -> anyhow::Result<(CustomGlyphData, Rc>)> {
+ let res = http_client::get_simple(executor, url).await?;
+ let glyph_data = CustomGlyphData::from_bytes_raster(globals, url, &res.data)?;
+ Ok((glyph_data, Rc::new(res.data)))
+}
diff --git a/dash-frontend/src/util/networking/mod.rs b/dash-frontend/src/util/networking/mod.rs
new file mode 100644
index 00000000..b6c625a8
--- /dev/null
+++ b/dash-frontend/src/util/networking/mod.rs
@@ -0,0 +1,6 @@
+pub mod http_client;
+pub mod image_fetch;
+pub mod skymap_catalog;
+
+// pub const WAYVR_ROOT_URL: &'static str = "https://wayvr.org";
+pub const WAYVR_SKYMAPS_ROOT: &'static str = "https://wayvr.org/skymaps";
diff --git a/dash-frontend/src/util/networking/skymap_catalog.rs b/dash-frontend/src/util/networking/skymap_catalog.rs
new file mode 100644
index 00000000..d7d21ca2
--- /dev/null
+++ b/dash-frontend/src/util/networking/skymap_catalog.rs
@@ -0,0 +1,195 @@
+use crate::util::networking::{self, WAYVR_SKYMAPS_ROOT, http_client};
+use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+use wlx_common::{async_executor::AsyncExecutor, config_io};
+pub type SkymapUuid = uuid::Uuid;
+
+#[derive(Copy, Clone, Serialize, Deserialize, Debug)]
+pub enum SkymapResolution {
+ Res2k,
+ Res4k,
+ Res8k,
+}
+
+impl SkymapResolution {
+ pub const fn get_display_str(&self) -> &'static str {
+ match self {
+ SkymapResolution::Res2k => "2K (2 MiB VRAM)",
+ SkymapResolution::Res4k => "4K (8 MiB VRAM)",
+ SkymapResolution::Res8k => "8K (33 MiB VRAM)",
+ }
+ }
+
+ pub const fn get_display_str_simple(&self) -> &'static str {
+ match self {
+ SkymapResolution::Res2k => "2K",
+ SkymapResolution::Res4k => "4K",
+ SkymapResolution::Res8k => "8K",
+ }
+ }
+
+ pub fn from_display_str_simple(text: &str) -> Option {
+ match text {
+ "2K" => Some(SkymapResolution::Res2k),
+ "4K" => Some(SkymapResolution::Res4k),
+ "8K" => Some(SkymapResolution::Res8k),
+ _ => None,
+ }
+ }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct SkymapCatalogEntryFiles {
+ pub size_8k: Option, // "my_skymap_8k.dds"
+ pub size_4k: Option, // "my_skymap_4k.dds"
+ pub size_2k: String, // we should have *at least* this
+ pub preview: String,
+}
+
+impl SkymapCatalogEntryFiles {
+ pub fn get_url_preview(&self) -> String {
+ format!("{}/files/{}", WAYVR_SKYMAPS_ROOT, self.preview)
+ }
+
+ pub fn get_filename_from_res(&self, res: SkymapResolution) -> Option {
+ match res {
+ SkymapResolution::Res2k => Some(&self.size_2k),
+ SkymapResolution::Res4k => self.size_4k.as_ref(),
+ SkymapResolution::Res8k => self.size_8k.as_ref(),
+ }
+ .map(|raw_filename| {
+ // sanitize filename, do not allow "../" just in case
+ PathBuf::from(raw_filename)
+ .file_name()
+ .map(|s| String::from(s.to_string_lossy()))
+ })?
+ }
+
+ // example result: "https://wayvr.org/skymaps/files/my_skymap_8k.dds"
+ pub fn get_url_from_res(&self, res: SkymapResolution) -> Option {
+ let Some(filename) = self.get_filename_from_res(res) else {
+ return None;
+ };
+
+ Some(format!("{}/files/{}", WAYVR_SKYMAPS_ROOT, filename))
+ }
+
+ pub fn get_preview_path(&self) -> PathBuf {
+ config_io::get_skymaps_root().join(&self.preview)
+ }
+
+ pub fn save_preview_to_file(&self, data: &[u8]) -> anyhow::Result<()> {
+ std::fs::write(self.get_preview_path(), data)?;
+ Ok(())
+ }
+
+ pub fn remove_preview_file(&self) {
+ let _dont_care = std::fs::remove_file(self.get_preview_path());
+ }
+}
+
+#[derive(Clone, Debug, Serialize, Deserialize)]
+pub struct SkymapCatalogEntry {
+ pub uuid: SkymapUuid,
+ pub created_at: String,
+ pub modified_at: String,
+ pub version: u32,
+ pub name: String,
+ pub description: String,
+ pub author: String,
+ pub files: SkymapCatalogEntryFiles,
+}
+
+impl SkymapCatalogEntry {
+ pub fn get_destination_path(&self, resolution: SkymapResolution) -> Option {
+ let Some(filename) = self.files.get_filename_from_res(resolution) else {
+ return None;
+ };
+
+ Some(config_io::get_skymaps_root().join(filename))
+ }
+
+ pub fn get_destination_metadata_path(&self) -> PathBuf {
+ config_io::get_skymaps_root().join(format!("{}.json", self.uuid))
+ }
+
+ pub fn is_downloaded(&self, resolution: SkymapResolution) -> anyhow::Result {
+ let Some(full_path) = self.get_destination_path(resolution) else {
+ return Ok(false);
+ };
+
+ Ok(std::fs::exists(full_path)?)
+ }
+
+ pub fn has_any_downloaded(&self) -> bool {
+ self.is_downloaded(SkymapResolution::Res2k).unwrap_or(false)
+ || self.is_downloaded(SkymapResolution::Res4k).unwrap_or(false)
+ || self.is_downloaded(SkymapResolution::Res8k).unwrap_or(false)
+ }
+
+ pub fn remove_file(&self, resolution: SkymapResolution) {
+ let Some(full_path) = self.get_destination_path(resolution) else {
+ return;
+ };
+
+ let _dont_care = std::fs::remove_file(full_path);
+ }
+
+ pub fn save_metadata(&self) -> anyhow::Result<()> {
+ let json = serde_json::to_string_pretty(self)?;
+ std::fs::write(self.get_destination_metadata_path(), json)?;
+ Ok(())
+ }
+
+ pub fn remove_metadata(&self) {
+ let _dont_care = std::fs::remove_file(self.get_destination_metadata_path());
+ }
+}
+
+#[derive(Clone, Debug, Deserialize)]
+pub struct SkymapCatalog {
+ pub version: u32,
+ pub r#type: String,
+ pub entries: Vec,
+}
+
+impl SkymapCatalog {
+ fn validate(&self) -> anyhow::Result<()> {
+ if self.version != 1 {
+ anyhow::bail!("Unsupported version");
+ }
+
+ if self.r#type != "wayvr_skymaps" {
+ anyhow::bail!("Unsupported type");
+ }
+ Ok(())
+ }
+}
+
+pub async fn request_catalog(executor: &AsyncExecutor) -> anyhow::Result {
+ log::info!("Fetching skymap list");
+
+ let res = http_client::get_simple(executor, &format!("{}/catalog.json", networking::WAYVR_SKYMAPS_ROOT)).await?;
+ let catalog = res.as_json::()?;
+ catalog.validate()?;
+
+ Ok(catalog)
+}
+
+pub fn get_entries_from_disk() -> anyhow::Result> {
+ let mut entries = Vec::::new();
+
+ let skymaps_root = config_io::get_skymaps_root();
+
+ for uuid in config_io::get_skymaps_uuids().unwrap_or_default() {
+ let metadata_path = skymaps_root.join(format!("{}.json", uuid));
+ let Ok(data) = std::fs::read_to_string(metadata_path) else {
+ continue;
+ };
+
+ let entry = serde_json::from_str::(&data)?;
+ entries.push(entry);
+ }
+
+ Ok(entries)
+}
diff --git a/dash-frontend/src/util/popup_manager.rs b/dash-frontend/src/util/popup_manager.rs
index ff46f515..fa7b7cfd 100644
--- a/dash-frontend/src/util/popup_manager.rs
+++ b/dash-frontend/src/util/popup_manager.rs
@@ -16,7 +16,10 @@ use wgui::{
};
use wlx_common::config::GeneralConfig;
-use crate::frontend::{FrontendTask, FrontendTasks};
+use crate::{
+ frontend::{FrontendTask, FrontendTasks},
+ views::{ViewTrait, ViewUpdateParams},
+};
pub struct PopupManagerParams {
pub parent_id: WidgetID,
@@ -34,15 +37,140 @@ pub struct MountedPopup {
frontend_tasks: FrontendTasks,
}
+#[derive(Default)]
struct MountedPopupState {
mounted_popup: Option,
+ closed_callback: Option,
}
-#[derive(Clone)]
+#[derive(Default, Clone)]
pub struct PopupHandle {
state: Rc>,
}
+struct PopupHolderState {
+ popup_handle: PopupHandle,
+ view: Option,
+ on_view_close: Option>,
+}
+
+// we can't use #[derive(Default)] due to the fact that ViewType can't be Default.
+impl Default for PopupHolderState {
+ fn default() -> Self {
+ Self {
+ popup_handle: Default::default(),
+ view: None,
+ on_view_close: None,
+ }
+ }
+}
+
+pub struct PopupHolder {
+ state: Rc>>,
+}
+
+impl Default for PopupHolder {
+ fn default() -> Self {
+ Self {
+ state: Rc::new(RefCell::new(PopupHolderState::default())),
+ }
+ }
+}
+
+impl PopupHolderState {
+ fn close(&mut self) {
+ if self.view.is_some() {
+ self.view = None;
+ if let Some(on_close) = self.on_view_close.take() {
+ on_close();
+ }
+ }
+ self.popup_handle.close();
+ }
+}
+
+impl Drop for PopupHolderState {
+ fn drop(&mut self) {
+ self.close();
+ }
+}
+
+// we can't derive(Clone) due to the fact that ViewType is non-cloneable
+impl Clone for PopupHolder {
+ fn clone(&self) -> Self {
+ Self {
+ state: self.state.clone(),
+ }
+ }
+}
+
+impl PopupHolder {
+ pub fn update(&self, par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ let mut state = self.state.borrow_mut();
+ let Some(view) = &mut state.view else {
+ return Ok(());
+ };
+
+ view.update(par)
+ }
+
+ pub fn set_view(&self, handle: PopupHandle, view: ViewType, on_view_close: Option>) {
+ let mut state = self.state.borrow_mut();
+ state.view = Some(view);
+ state.popup_handle = handle;
+ state.on_view_close = on_view_close;
+ }
+
+ // Get underlying ViewType object in a closure and return its value
+ // example usage:
+ //
+ // ```rs
+ // holder.with_view(|view| {
+ // view.foo();
+ // })
+ // ```
+ //
+ pub fn with_view(&self, f: F) -> Option
+ where
+ F: FnOnce(&mut ViewType) -> R,
+ {
+ let mut state = self.state.borrow_mut();
+ if let Some(view) = state.view.as_mut() {
+ Some(f(view))
+ } else {
+ None
+ }
+ }
+
+ // Same as with_view, but the closure expects a simple anyhow::Result<()> type
+ pub fn with_view_res(&self, f: F) -> anyhow::Result<()>
+ where
+ F: FnOnce(&mut ViewType) -> anyhow::Result<()>,
+ {
+ if let Some(res) = self.with_view(f) {
+ return res;
+ }
+ Ok(())
+ }
+
+ pub fn get_close_callback(&self, layout: &Layout) -> Box
+ where
+ ViewType: 'static,
+ {
+ let layout_tasks = layout.tasks.clone();
+ let weak_state = Rc::downgrade(&self.state);
+ Box::new(move || {
+ // we can't borrow State here yet, dispatch it.
+ layout_tasks.push(LayoutTask::Dispatch(Box::new(move |_common| {
+ if let Some(state) = weak_state.upgrade() {
+ state.borrow_mut().close();
+ }
+ Ok(())
+ })));
+ })
+ }
+}
+
impl PopupHandle {
pub fn close(&self) {
self.state.borrow_mut().mounted_popup = None; // Drop will be called
@@ -61,10 +189,26 @@ pub struct PopupContentFuncData<'a> {
pub id_content: WidgetID,
}
+type PopupClosedCallback = Box;
+
+// we need to implement Clone here, but the underlying function can be called only once.
+// on_content will be cleared after the first call
#[derive(Clone)]
-pub struct MountPopupParams {
- pub title: Translation,
- pub on_content: Rc anyhow::Result<()>>,
+pub struct MountPopupOnceParams {
+ title: Translation,
+ on_content: Rc anyhow::Result>>>>,
+}
+
+impl MountPopupOnceParams {
+ pub fn new(
+ title: Translation,
+ on_content: Box anyhow::Result>,
+ ) -> Self {
+ Self {
+ title,
+ on_content: Rc::new(RefCell::new(Some(on_content))),
+ }
+ }
}
impl Drop for MountedPopup {
@@ -78,10 +222,16 @@ impl State {
fn refresh_stack(&mut self, alterables: &mut EventAlterables) {
// show only the topmost popup
self.popup_stack.retain(|weak| {
- let Some(popup) = weak.upgrade() else {
- return false;
+ let retain = {
+ let Some(popup) = weak.upgrade() else {
+ return false;
+ };
+ popup.borrow_mut().mounted_popup.is_some()
};
- popup.borrow_mut().mounted_popup.is_some()
+ if !retain {
+ log::debug!("removing popup from popup_stack");
+ }
+ retain
});
for (idx, popup) in self.popup_stack.iter().enumerate() {
@@ -116,16 +266,13 @@ impl PopupManager {
state.refresh_stack(alterables);
}
- /// Mount a new popup on top of the existing popup stack.
- /// Only the topmost popup is visible.
- pub fn mount_popup(
- &mut self,
- globals: WguiGlobals,
+ fn mount_popup_prepare(
+ &self,
+ globals: &WguiGlobals,
layout: &mut Layout,
- frontend_tasks: FrontendTasks,
- params: MountPopupParams,
- config: &GeneralConfig,
- ) -> anyhow::Result<()> {
+ frontend_tasks: &FrontendTasks,
+ popup_title: &Translation,
+ ) -> anyhow::Result<(PopupHandle, WidgetID /* content widget ID */)> {
let doc_params = &ParseDocumentParams {
globals: globals.clone(),
path: AssetPath::BuiltIn("gui/view/popup_window.xml"),
@@ -138,7 +285,7 @@ impl PopupManager {
{
let mut label_title = state.fetch_widget_as::(&layout.state, "popup_title")?;
- label_title.set_text_simple(&mut globals.get(), params.title);
+ label_title.set_text_simple(&mut globals.get(), popup_title.clone());
}
let but_back = state.fetch_component_as::("but_back")?;
@@ -152,6 +299,7 @@ impl PopupManager {
let mounted_popup_state = MountedPopupState {
mounted_popup: Some(mounted_popup),
+ closed_callback: None,
};
let popup_handle = PopupHandle {
@@ -159,28 +307,57 @@ impl PopupManager {
};
let mut state = self.state.borrow_mut();
+ log::debug!("pushing popup to popup_stack");
state.popup_stack.push(Rc::downgrade(&popup_handle.state));
but_back.on_click({
let popup_handle = Rc::downgrade(&popup_handle.state);
Rc::new(move |_common, _evt| {
if let Some(popup_handle) = popup_handle.upgrade() {
- popup_handle.borrow_mut().mounted_popup = None; // will call Drop
+ if let Some(closed_callback) = {
+ let mut state = popup_handle.borrow_mut();
+ state.mounted_popup = None; // will call Drop
+ state.closed_callback.take()
+ } {
+ log::debug!("closed_callback called");
+ closed_callback();
+ }
}
Ok(())
})
});
frontend_tasks.push(FrontendTask::RefreshPopupManager);
+ Ok((popup_handle, id_content))
+ }
+
+ /// Mount a new popup on top of the existing popup stack.
+ /// Only the topmost popup is visible.
+ pub fn mount_popup_once(
+ &mut self,
+ globals: &WguiGlobals,
+ layout: &mut Layout,
+ frontend_tasks: &FrontendTasks,
+ params: MountPopupOnceParams,
+ config: &GeneralConfig,
+ ) -> anyhow::Result<()> {
+ let mut func = params.on_content.borrow_mut();
+ let Some(on_content_func) = func.take() else {
+ anyhow::bail!("mount_popup_once called more than once");
+ };
+
+ let (popup_handle, id_content) = self.mount_popup_prepare(globals, layout, frontend_tasks, ¶ms.title)?;
// mount user-set popup content
- (*params.on_content)(PopupContentFuncData {
+ let closed_callback = on_content_func(PopupContentFuncData {
layout,
handle: popup_handle.clone(),
id_content,
config,
})?;
+ popup_handle.state.borrow_mut().closed_callback = Some(closed_callback);
+
Ok(())
}
}
diff --git a/dash-frontend/src/util/steam_utils.rs b/dash-frontend/src/util/steam_utils.rs
index a2c6e778..3c7faa3a 100644
--- a/dash-frontend/src/util/steam_utils.rs
+++ b/dash-frontend/src/util/steam_utils.rs
@@ -2,6 +2,7 @@ use keyvalues_parser::{Obj, Vdf};
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
+#[derive(Clone)]
pub struct SteamUtils {
steam_root: PathBuf,
}
diff --git a/dash-frontend/src/util/wgui_simple.rs b/dash-frontend/src/util/wgui_simple.rs
index 44dad5ee..af4d393c 100644
--- a/dash-frontend/src/util/wgui_simple.rs
+++ b/dash-frontend/src/util/wgui_simple.rs
@@ -1,12 +1,52 @@
+use glam::{Mat4, Vec2};
use wgui::{
+ animation::{Animation, AnimationEasing},
+ assets::AssetPath,
+ components::{self, button::ButtonClickCallback},
+ drawing,
i18n::Translation,
- layout::{Layout, WidgetID},
- renderer_vk::text::TextStyle,
- widget::label::{WidgetLabel, WidgetLabelParams},
+ layout::{Layout, LayoutTask, WidgetID},
+ parser::{Fetchable, ParseDocumentParams},
+ renderer_vk::{
+ text::{FontWeight, TextStyle, custom_glyph::CustomGlyphData},
+ util::centered_matrix,
+ },
+ taffy::{self, prelude::length},
+ widget::{
+ ConstructEssentials,
+ label::{WidgetLabel, WidgetLabelParams},
+ sprite::{WidgetSprite, WidgetSpriteParams},
+ },
};
+pub struct CreateButtonParams<'a> {
+ pub id_parent: WidgetID,
+ pub layout: &'a mut Layout,
+ pub content: Translation,
+ pub icon_builtin: AssetPath<'a>,
+ pub on_click: ButtonClickCallback,
+}
+
+pub fn create_button(par: CreateButtonParams) -> anyhow::Result<()> {
+ let (_, button) = components::button::construct(
+ &mut ConstructEssentials {
+ layout: par.layout,
+ parent: par.id_parent,
+ },
+ components::button::Params {
+ text: Some(par.content),
+ sprite_src: Some(par.icon_builtin),
+ ..Default::default()
+ },
+ )?;
+
+ button.on_click(par.on_click);
+
+ Ok(())
+}
+
#[allow(dead_code)]
-pub fn create_label(layout: &mut Layout, parent: WidgetID, content: Translation) -> anyhow::Result<()> {
+pub fn create_label(layout: &mut Layout, id_parent: WidgetID, content: Translation) -> anyhow::Result<()> {
let label = WidgetLabel::create(
&mut layout.state,
WidgetLabelParams {
@@ -18,7 +58,102 @@ pub fn create_label(layout: &mut Layout, parent: WidgetID, content: Translation)
},
);
+ layout.add_child(id_parent, label, Default::default())?;
+
+ Ok(())
+}
+
+pub fn create_label_error(layout: &mut Layout, parent: WidgetID, content: String) -> anyhow::Result<()> {
+ let label = WidgetLabel::create(
+ &mut layout.state,
+ WidgetLabelParams {
+ content: Translation::from_raw_text_string(content),
+ style: TextStyle {
+ wrap: true,
+ color: Some(drawing::Color::new(1.0, 0.5, 0.0, 1.0)),
+ weight: Some(FontWeight::Bold),
+ ..Default::default()
+ },
+ },
+ );
+
layout.add_child(parent, label, Default::default())?;
Ok(())
}
+
+pub fn create_icon(layout: &mut Layout, id_parent: WidgetID, size: Vec2, path: AssetPath) -> anyhow::Result {
+ let widget_sprite = WidgetSprite::create(WidgetSpriteParams {
+ color: None,
+ glyph_data: Some(CustomGlyphData::from_assets(&layout.state.globals, path)?),
+ });
+
+ let size = taffy::Size {
+ width: length(size.x),
+ height: length(size.y),
+ };
+
+ let (widget, _) = layout.add_child(
+ id_parent,
+ widget_sprite,
+ taffy::Style {
+ min_size: size.clone(),
+ max_size: size.clone(),
+ size: size.clone(),
+ ..Default::default()
+ },
+ )?;
+
+ Ok(widget.id)
+}
+
+pub struct CreateLoadingParams<'a> {
+ pub layout: &'a mut Layout,
+ pub parent_id: WidgetID,
+ pub with_text: bool,
+}
+
+pub fn create_loading(par: CreateLoadingParams) -> anyhow::Result {
+ let doc_params = ParseDocumentParams {
+ globals: par.layout.state.globals.clone(),
+ path: AssetPath::BuiltIn("gui/t_loading.xml"),
+ extra: Default::default(),
+ };
+
+ let mut parser_state = wgui::parser::parse_from_assets(&doc_params, par.layout, par.parent_id)?;
+
+ let data = parser_state.realize_template(
+ &doc_params,
+ if par.with_text {
+ "LoadingWithText"
+ } else {
+ "LoadingWithoutText"
+ },
+ par.layout,
+ par.parent_id,
+ Default::default(),
+ )?;
+
+ let id_root = data.get_widget_id("root")?;
+ let id_sprite_loading = data.get_widget_id("sprite_loading")?;
+
+ par.layout.animations.add(Animation::new(
+ id_sprite_loading,
+ 60 * 30, /* spin it for 30 seconds at most */
+ AnimationEasing::Linear,
+ Box::new(move |common, data| {
+ // spin it
+ data.data.transform = centered_matrix(data.widget_boundary.size, &Mat4::from_rotation_z(data.pos * 400.0));
+ if data.pos == 1.0 {
+ // remove the spinner, do not waste energy
+ common
+ .alterables
+ .tasks
+ .push(LayoutTask::RemoveWidget(id_sprite_loading));
+ }
+ common.alterables.mark_redraw();
+ }),
+ ));
+
+ Ok(id_root)
+}
diff --git a/dash-frontend/src/views/app_launcher.rs b/dash-frontend/src/views/app_launcher.rs
index 76c5b524..af9c398b 100644
--- a/dash-frontend/src/views/app_launcher.rs
+++ b/dash-frontend/src/views/app_launcher.rs
@@ -14,7 +14,11 @@ use wgui::{
};
use wlx_common::{config::GeneralConfig, dash_interface::BoxDashInterface, desktop_finder::DesktopEntry};
-use crate::frontend::{FrontendTask, FrontendTasks, SoundType};
+use crate::{
+ frontend::{FrontendTask, FrontendTasks, SoundType},
+ util::popup_manager::{MountPopupOnceParams, PopupHolder},
+ views::{ViewTrait, ViewUpdateParams},
+};
#[derive(Clone, Copy, Eq, PartialEq, EnumString, VariantNames, AsRefStr)]
enum PosMode {
@@ -66,7 +70,7 @@ struct LaunchParams<'a, T> {
interface: &'a mut BoxDashInterface,
auto_start: bool,
data: &'a mut T,
- on_launched: &'a dyn Fn(),
+ on_launched: Option>,
}
pub struct View {
@@ -91,7 +95,7 @@ pub struct View {
auto_start: bool,
- on_launched: Box,
+ on_launched: Option>,
}
pub struct Params<'a> {
@@ -101,7 +105,13 @@ pub struct Params<'a> {
pub parent_id: WidgetID,
pub config: &'a GeneralConfig,
pub frontend_tasks: &'a FrontendTasks,
- pub on_launched: Box,
+ pub on_launched: Box,
+}
+
+impl ViewTrait for View {
+ fn update(&mut self, _par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ Ok(())
+ }
}
impl View {
@@ -280,7 +290,7 @@ impl View {
entry: params.entry,
frontend_tasks: params.frontend_tasks.clone(),
globals: params.globals.clone(),
- on_launched: params.on_launched,
+ on_launched: Some(params.on_launched),
})
}
@@ -316,7 +326,7 @@ impl View {
auto_start: self.auto_start,
interface,
data,
- on_launched: &self.on_launched,
+ on_launched: self.on_launched.take(),
});
}
@@ -334,7 +344,7 @@ impl View {
))));
}
- fn launch(params: LaunchParams) -> anyhow::Result<()> {
+ fn launch(mut params: LaunchParams) -> anyhow::Result<()> {
let mut env = Vec::::new();
if params.compositor_mode == CompositorMode::Native {
@@ -390,7 +400,9 @@ impl View {
params.frontend_tasks.push(FrontendTask::PlaySound(SoundType::Launch));
- (*params.on_launched)();
+ if let Some(on_launched) = params.on_launched.take() {
+ on_launched();
+ }
// we're done!
Ok(())
@@ -420,3 +432,26 @@ impl View {
[width as u32, height as u32]
}
}
+
+pub fn mount_popup(frontend_tasks: FrontendTasks, globals: WguiGlobals, entry: DesktopEntry, popup: PopupHolder) {
+ frontend_tasks
+ .clone()
+ .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
+ Translation::from_raw_text(&entry.app_name),
+ Box::new(move |data| {
+ let on_launched = popup.get_close_callback(data.layout);
+ let view = View::new(Params {
+ entry: entry.clone(),
+ globals: &globals,
+ layout: data.layout,
+ parent_id: data.id_content,
+ frontend_tasks: &frontend_tasks,
+ config: data.config,
+ on_launched,
+ })?;
+
+ popup.set_view(data.handle, view, None);
+ Ok(popup.get_close_callback(data.layout))
+ }),
+ )));
+}
diff --git a/dash-frontend/src/views/audio_settings.rs b/dash-frontend/src/views/audio_settings.rs
index 8284ea50..9546d961 100644
--- a/dash-frontend/src/views/audio_settings.rs
+++ b/dash-frontend/src/views/audio_settings.rs
@@ -653,9 +653,6 @@ impl View {
}
fn update_button_highlights(&self, layout: &mut Layout) -> anyhow::Result<()> {
- let mut c = layout.start_common();
- let mut common = c.common();
-
let num: u8 = match &self.mode {
CurrentMode::Sinks => 0,
CurrentMode::Sources => 1,
@@ -663,20 +660,21 @@ impl View {
CurrentMode::CardProfileSelector(_) => 255,
};
+ let mut com = layout.common();
+
let mut perform = |btn_num: u8, btn: &Rc| {
let color = if num == btn_num {
- common.state.theme.accent_color
+ com.state.theme.accent_color
} else {
- common.state.theme.button_color
+ com.state.theme.button_color
};
- btn.set_color(&mut common, color);
+ btn.set_color(&mut com, color);
};
perform(0, &self.btn_sinks);
perform(1, &self.btn_sources);
perform(2, &self.btn_cards);
- c.finish()?;
Ok(())
}
@@ -801,14 +799,12 @@ impl View {
par,
)?;
- let mut c = params.layout.start_common();
- let mut common = c.common();
-
+ let mut common = params.layout.common();
let checkbox = data.fetch_component_as::("checkbox")?;
let btn_mute = data.fetch_component_as::("btn_mute")?;
let slider = data.fetch_component_as::("slider")?;
- slider.set_value(&mut common, params.control.on_volume_request()? / VOLUME_MULT);
+ slider.set_value_primary(&mut common, params.control.on_volume_request()? / VOLUME_MULT);
checkbox.set_checked(&mut common, params.checked);
@@ -838,8 +834,6 @@ impl View {
})
});
- c.finish()?;
-
Ok(())
}
diff --git a/dash-frontend/src/views/dialog_box.rs b/dash-frontend/src/views/dialog_box.rs
new file mode 100644
index 00000000..eba712af
--- /dev/null
+++ b/dash-frontend/src/views/dialog_box.rs
@@ -0,0 +1,127 @@
+use std::{collections::HashMap, rc::Rc};
+
+use crate::{
+ frontend::{FrontendTask, FrontendTasks},
+ util::popup_manager::{MountPopupOnceParams, PopupHolder},
+ views::{ViewTrait, ViewUpdateParams},
+};
+use wgui::{
+ assets::AssetPath,
+ components::button::ComponentButton,
+ globals::WguiGlobals,
+ i18n::Translation,
+ layout::{Layout, WidgetID},
+ parser::{Fetchable, ParseDocumentParams, ParserState},
+ task::Tasks,
+ widget::label::WidgetLabel,
+};
+
+pub struct ButtonEntry {
+ pub content: Translation, // button text
+ pub icon: &'static str, // sprite_src_builtin
+ pub action: &'static str, // action name (will be passed into on_action_click)
+}
+
+pub struct Params {
+ pub globals: WguiGlobals,
+ pub entries: Vec,
+ pub message: Translation,
+ pub on_action_click: Box,
+}
+
+#[derive(Clone)]
+enum Task {
+ ActionClicked(&'static str),
+}
+
+pub struct View {
+ tasks: Tasks,
+
+ #[allow(dead_code)]
+ parser_state: ParserState,
+
+ on_action_click: Option>,
+ on_close_request: Option>,
+}
+
+fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> {
+ ParseDocumentParams {
+ globals: globals.clone(),
+ path: AssetPath::BuiltIn("gui/view/dialog_box.xml"),
+ extra: Default::default(),
+ }
+}
+
+impl ViewTrait for View {
+ fn update(&mut self, _par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ for task in self.tasks.drain() {
+ match task {
+ Task::ActionClicked(action) => {
+ if let Some(func) = self.on_action_click.take() {
+ func(action);
+ }
+
+ if let Some(on_close) = self.on_close_request.take() {
+ on_close();
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+impl View {
+ pub fn new(
+ layout: &mut Layout,
+ id_parent: WidgetID,
+ on_close_request: Box,
+ par: Params,
+ ) -> anyhow::Result {
+ let tasks = Tasks::::new();
+
+ let mut parser_state = wgui::parser::parse_from_assets(&doc_params(&par.globals), layout, id_parent)?;
+ let id_buttons = parser_state.get_widget_id("buttons")?;
+
+ {
+ let label_message = parser_state.fetch_widget(&layout.state, "label_message")?.widget;
+ label_message
+ .cast::()?
+ .set_text(&mut layout.common(), par.message);
+ }
+
+ for entry in par.entries {
+ let mut t_par = HashMap::, Rc>::new();
+ t_par.insert(Rc::from("icon"), Rc::from(entry.icon));
+
+ let data =
+ parser_state.realize_template(&doc_params(&par.globals), "DialogBoxButton", layout, id_buttons, t_par)?;
+
+ let button = data.fetch_component_as::("btn")?;
+ button.set_text(&mut layout.common(), entry.content.clone());
+ button.on_click(tasks.get_button_click_callback(Task::ActionClicked(entry.action)));
+ }
+
+ Ok(Self {
+ tasks,
+ parser_state,
+ on_action_click: Some(par.on_action_click),
+ on_close_request: Some(on_close_request),
+ })
+ }
+}
+
+pub fn mount_popup(popup: PopupHolder, frontend_tasks: FrontendTasks, params: Params) {
+ frontend_tasks
+ .clone()
+ .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
+ Translation::from_raw_text("Info"),
+ Box::new(move |data| {
+ let on_close_request = popup.get_close_callback(data.layout);
+ let view = View::new(data.layout, data.id_content, on_close_request, params)?;
+
+ popup.set_view(data.handle, view, None);
+ Ok(popup.get_close_callback(data.layout))
+ }),
+ )));
+}
diff --git a/dash-frontend/src/views/download_file.rs b/dash-frontend/src/views/download_file.rs
new file mode 100644
index 00000000..30b299ff
--- /dev/null
+++ b/dash-frontend/src/views/download_file.rs
@@ -0,0 +1,265 @@
+use crate::{
+ frontend::{FrontendTask, FrontendTasks},
+ util::{
+ networking::http_client::{self, ProgressFuncData},
+ popup_manager::{MountPopupOnceParams, PopupHolder},
+ wgui_simple,
+ },
+ views::{ViewTrait, ViewUpdateParams},
+};
+use glam::Vec2;
+use std::path::PathBuf;
+use wgui::{
+ assets::AssetPath,
+ components::button::ComponentButton,
+ globals::WguiGlobals,
+ i18n::Translation,
+ layout::{Layout, WidgetID},
+ parser::{Fetchable, ParseDocumentParams, ParserState},
+ task::Tasks,
+ widget::label::WidgetLabel,
+};
+use wlx_common::async_executor::AsyncExecutor;
+
+pub struct Params {
+ pub globals: WguiGlobals,
+ pub executor: AsyncExecutor,
+ pub target_path: PathBuf,
+ pub url: String,
+ pub on_downloaded: Box,
+}
+
+#[derive(Clone)]
+enum Task {
+ StartDownload(/*url*/ String, /*target path*/ PathBuf),
+ SetStatusText(String),
+ ShowIconSuccess,
+ ShowIconError,
+ Close,
+}
+
+pub struct View {
+ globals: WguiGlobals,
+ tasks: Tasks,
+ executor: AsyncExecutor,
+
+ #[allow(dead_code)]
+ parser_state: ParserState,
+
+ id_label_status: WidgetID,
+ id_loading_parent: WidgetID,
+ id_content: WidgetID,
+ on_close_request: Option>,
+ on_downloaded: Option>,
+}
+
+fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> {
+ ParseDocumentParams {
+ globals: globals.clone(),
+ path: AssetPath::BuiltIn("gui/view/download_file.xml"),
+ extra: Default::default(),
+ }
+}
+
+impl ViewTrait for View {
+ fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ for task in self.tasks.drain() {
+ match task {
+ Task::StartDownload(url, path) => {
+ if let Some(on_downloaded) = self.on_downloaded.take() {
+ self
+ .executor
+ .spawn(View::download(
+ self.tasks.clone(),
+ self.executor.clone(),
+ url,
+ path,
+ on_downloaded,
+ ))
+ .detach();
+ }
+ }
+ Task::SetStatusText(text) => {
+ let widgets = &mut par.layout.state.widgets;
+ widgets
+ .fetch(self.id_label_status)?
+ .cast::()?
+ .set_text(&mut par.layout.common(), Translation::from_raw_text_string(text));
+ }
+ Task::ShowIconSuccess => {
+ par.layout.remove_children(self.id_loading_parent);
+ wgui_simple::create_icon(
+ par.layout,
+ self.id_loading_parent,
+ Vec2::splat(32.0),
+ AssetPath::BuiltIn("dashboard/check.svg"),
+ )?;
+
+ // "Close window" button
+ self
+ .parser_state
+ .realize_template(
+ &doc_params(&self.globals),
+ "btn_close",
+ par.layout,
+ self.id_content,
+ Default::default(),
+ )?
+ .fetch_component_as::("btn")?
+ .on_click(self.tasks.get_button_click_callback(Task::Close));
+ }
+ Task::ShowIconError => {
+ par.layout.remove_children(self.id_loading_parent);
+ wgui_simple::create_icon(
+ par.layout,
+ self.id_loading_parent,
+ Vec2::splat(32.0),
+ AssetPath::BuiltIn("dashboard/error.svg"),
+ )?;
+ }
+ Task::Close => {
+ if let Some(on_close) = self.on_close_request.take() {
+ on_close();
+ }
+ }
+ }
+ }
+ Ok(())
+ }
+}
+
+fn handle_async_result(error_reason: &'static str, tasks: &Tasks, result: anyhow::Result) -> Option
+where
+ E: std::fmt::Debug,
+{
+ match result {
+ Ok(res) => Some(res),
+ Err(e) => {
+ tasks.push(Task::ShowIconError);
+ tasks.push(Task::SetStatusText(format!("{}: {:?}", error_reason, e)));
+ None
+ }
+ }
+}
+
+impl View {
+ pub fn new(
+ layout: &mut Layout,
+ id_parent: WidgetID,
+ on_close_request: Box,
+ par: Params,
+ ) -> anyhow::Result {
+ let tasks = Tasks::::new();
+
+ let parser_state = wgui::parser::parse_from_assets(&doc_params(&par.globals), layout, id_parent)?;
+ let id_label_status = parser_state.get_widget_id("label_status")?;
+ let id_content = parser_state.get_widget_id("content")?;
+ let id_loading_parent = parser_state.get_widget_id("loading_parent")?;
+
+ wgui_simple::create_loading(wgui_simple::CreateLoadingParams {
+ parent_id: id_loading_parent,
+ layout: layout,
+ with_text: false,
+ })?;
+
+ let str_target_path = par.globals.i18n().translate("TARGET_PATH");
+
+ {
+ let label_target_path = parser_state.fetch_widget(&layout.state, "label_target_path")?.widget;
+ label_target_path.cast::()?.set_text(
+ &mut layout.common(),
+ Translation::from_raw_text_string(format!("{}: {}", str_target_path, par.target_path.display())),
+ );
+ }
+
+ tasks.push(Task::StartDownload(par.url, par.target_path));
+
+ Ok(Self {
+ tasks,
+ globals: par.globals.clone(),
+ executor: par.executor.clone(),
+ parser_state,
+ id_label_status,
+ id_loading_parent,
+ id_content,
+ on_close_request: Some(on_close_request),
+ on_downloaded: Some(par.on_downloaded),
+ })
+ }
+
+ async fn download(
+ tasks: Tasks,
+ executor: AsyncExecutor,
+ url: String,
+ target_path: PathBuf,
+ on_downloaded: Box,
+ ) -> Option<()> {
+ tasks.push(Task::SetStatusText(String::from("Connecting to the server...")));
+
+ // start downloading from the server with progress reporting
+ let res = handle_async_result(
+ "Download failed",
+ &tasks,
+ http_client::get(http_client::GetParams {
+ executor: &executor,
+ url: &url,
+ on_progress: Some(Box::new({
+ let tasks = tasks.clone();
+ move |data: ProgressFuncData| {
+ tasks.push(Task::SetStatusText(format!(
+ "{}/{} KiB ({}%)",
+ data.bytes_downloaded / 1024,
+ data.file_size / 1024,
+ (data.bytes_downloaded as f32 / data.file_size as f32 * 100.0).round()
+ )))
+ }
+ })),
+ })
+ .await,
+ )?;
+
+ tasks.push(Task::SetStatusText(String::from("Writing to file...")));
+
+ // create skymaps directory if it doesn't exist yet
+ if let Some(parent) = target_path.parent() {
+ handle_async_result(
+ "Directory creation failed",
+ &tasks,
+ smol::fs::create_dir_all(parent).await,
+ )?;
+ }
+
+ handle_async_result(
+ "File write failed",
+ &tasks,
+ smol::fs::write(target_path, res.data).await,
+ )?;
+
+ tasks.push(Task::SetStatusText(String::from("Download finished")));
+ tasks.push(Task::ShowIconSuccess);
+
+ on_downloaded();
+
+ None
+ }
+}
+
+pub fn mount_popup(
+ popup: PopupHolder,
+ frontend_tasks: FrontendTasks,
+ on_view_close: Box,
+ params: Params,
+) {
+ frontend_tasks
+ .clone()
+ .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
+ Translation::from_translation_key("DOWNLOADER"),
+ Box::new(move |data| {
+ let on_close_request = popup.get_close_callback(data.layout);
+ let view = View::new(data.layout, data.id_content, on_close_request, params)?;
+
+ popup.set_view(data.handle, view, Some(on_view_close));
+ Ok(popup.get_close_callback(data.layout))
+ }),
+ )));
+}
diff --git a/dash-frontend/src/views/game_cover.rs b/dash-frontend/src/views/game_cover.rs
index 6adc4110..5258b041 100644
--- a/dash-frontend/src/views/game_cover.rs
+++ b/dash-frontend/src/views/game_cover.rs
@@ -30,6 +30,7 @@ use wlx_common::async_executor::AsyncExecutor;
use crate::util::{
cached_fetcher::{self, CoverArt},
steam_utils::{self, AppID},
+ wgui_simple,
};
pub struct ViewCommon {
@@ -48,6 +49,7 @@ pub struct Params<'a, 'b> {
pub struct View {
pub button: Rc,
id_image_parent: WidgetID,
+ id_loading: WidgetID,
app_name: String,
app_id: AppID,
}
@@ -143,6 +145,8 @@ impl View {
layout: &mut Layout,
cover_art: &CoverArt,
) -> anyhow::Result<()> {
+ layout.remove_widget(self.id_loading);
+
if cover_art.compressed_image_data.is_empty() {
// mount placeholder
let img = view_common.get_placeholder_image()?.clone();
@@ -271,6 +275,12 @@ impl View {
rect_gradient_style(taffy::AlignSelf::End, 0.05),
)?;
+ let id_loading = wgui_simple::create_loading(wgui_simple::CreateLoadingParams {
+ layout: params.ess.layout,
+ parent_id: image_parent.id,
+ with_text: false,
+ })?;
+
// request cover image data from the internet or disk cache
params
.executor
@@ -286,6 +296,7 @@ impl View {
id_image_parent: image_parent.id,
app_name: params.manifest.name.clone(),
app_id: params.manifest.app_id.clone(),
+ id_loading,
})
}
}
diff --git a/dash-frontend/src/views/game_launcher.rs b/dash-frontend/src/views/game_launcher.rs
index 03d26d77..17722bc1 100644
--- a/dash-frontend/src/views/game_launcher.rs
+++ b/dash-frontend/src/views/game_launcher.rs
@@ -4,9 +4,10 @@ use crate::{
frontend::{FrontendTask, FrontendTasks, SoundType},
util::{
cached_fetcher::{self, CoverArt},
+ popup_manager::{MountPopupOnceParams, PopupHolder},
steam_utils::{self, AppID, AppManifest},
},
- views::game_cover,
+ views::{ViewTrait, ViewUpdateParams, game_cover},
};
use wgui::{
assets::AssetPath,
@@ -34,13 +35,14 @@ pub struct Params<'a> {
pub layout: &'a mut Layout,
pub parent_id: WidgetID,
pub frontend_tasks: &'a FrontendTasks,
- pub on_launched: Box,
+ pub on_launched: Box,
}
+
pub struct View {
#[allow(dead_code)]
state: ParserState,
tasks: Tasks,
- on_launched: Box,
+ on_launched: Option>,
frontend_tasks: FrontendTasks,
game_cover_view_common: game_cover::ViewCommon,
@@ -48,6 +50,30 @@ pub struct View {
app_id: AppID,
}
+impl ViewTrait for View {
+ fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ loop {
+ let tasks = self.tasks.drain();
+ if tasks.is_empty() {
+ break;
+ }
+ for task in tasks {
+ match task {
+ Task::FillAppDetails(details) => self.action_fill_app_details(&mut par.layout, details)?,
+ Task::Launch => self.action_launch(),
+ Task::SetCoverArt(cover_art) => {
+ let _ = self
+ .view_cover
+ .set_cover_art(&mut self.game_cover_view_common, &mut par.layout, &cover_art);
+ }
+ }
+ }
+ }
+
+ Ok(())
+ }
+}
+
impl View {
async fn fetch_details(executor: AsyncExecutor, tasks: Tasks, app_id: AppID) {
let Some(details) = cached_fetcher::get_app_details_json(executor, app_id).await else {
@@ -104,7 +130,7 @@ impl View {
Ok(Self {
state,
tasks,
- on_launched: params.on_launched,
+ on_launched: Some(params.on_launched),
frontend_tasks: params.frontend_tasks.clone(),
game_cover_view_common: game_cover::ViewCommon::new(params.globals.clone()),
view_cover,
@@ -112,43 +138,20 @@ impl View {
})
}
- pub fn update(&mut self, layout: &mut Layout) -> anyhow::Result<()> {
- loop {
- let tasks = self.tasks.drain();
- if tasks.is_empty() {
- break;
- }
- for task in tasks {
- match task {
- Task::FillAppDetails(details) => self.action_fill_app_details(layout, details)?,
- Task::Launch => self.action_launch(),
- Task::SetCoverArt(cover_art) => {
- let _ = self
- .view_cover
- .set_cover_art(&mut self.game_cover_view_common, layout, &cover_art);
- }
- }
- }
- }
-
- Ok(())
- }
-
fn action_fill_app_details(
&mut self,
layout: &mut Layout,
mut details: cached_fetcher::AppDetailsJSONData,
) -> anyhow::Result<()> {
- let mut c = layout.start_common();
-
{
- let label_author = self.state.fetch_widget(&c.layout.state, "label_author")?.widget;
- let label_description = self.state.fetch_widget(&c.layout.state, "label_description")?.widget;
+ let mut c = layout.common();
+ let label_author = self.state.fetch_widget(&c.state, "label_author")?.widget;
+ let label_description = self.state.fetch_widget(&c.state, "label_description")?.widget;
if let Some(developer) = details.developers.pop() {
label_author
.cast::()?
- .set_text(&mut c.common(), Translation::from_raw_text_string(developer));
+ .set_text(&mut c, Translation::from_raw_text_string(developer));
}
let desc = if let Some(desc) = &details.short_description {
@@ -162,11 +165,10 @@ impl View {
if let Some(desc) = desc {
label_description
.cast::()?
- .set_text(&mut c.common(), Translation::from_raw_text(desc));
+ .set_text(&mut c, Translation::from_raw_text(desc));
}
}
- c.finish()?;
Ok(())
}
@@ -190,6 +192,37 @@ impl View {
}
}
- (*self.on_launched)();
+ if let Some(on_launched) = self.on_launched.take() {
+ on_launched();
+ }
}
}
+
+pub fn mount_popup(
+ frontend_tasks: FrontendTasks,
+ executor: AsyncExecutor,
+ globals: WguiGlobals,
+ manifest: AppManifest,
+ popup: PopupHolder,
+) {
+ frontend_tasks
+ .clone()
+ .push(FrontendTask::MountPopupOnce(MountPopupOnceParams::new(
+ Translation::from_raw_text(&manifest.name),
+ Box::new(move |data| {
+ let on_launched = popup.get_close_callback(data.layout);
+ let view = View::new(Params {
+ manifest: manifest.clone(),
+ executor: executor.clone(),
+ globals: &globals,
+ layout: data.layout,
+ parent_id: data.id_content,
+ frontend_tasks: &frontend_tasks,
+ on_launched,
+ })?;
+
+ popup.set_view(data.handle, view, None);
+ Ok(popup.get_close_callback(data.layout))
+ }),
+ )));
+}
diff --git a/dash-frontend/src/views/game_list.rs b/dash-frontend/src/views/game_list.rs
index 5de3cf64..ab7e6448 100644
--- a/dash-frontend/src/views/game_list.rs
+++ b/dash-frontend/src/views/game_list.rs
@@ -1,4 +1,4 @@
-use std::{cell::RefCell, collections::HashMap, rc::Rc};
+use std::{collections::HashMap, rc::Rc};
use wgui::{
assets::AssetPath,
@@ -16,20 +16,19 @@ use wgui::{
use wlx_common::async_executor::AsyncExecutor;
use crate::{
- frontend::{FrontendTask, FrontendTasks},
+ frontend::FrontendTasks,
util::{
cached_fetcher::CoverArt,
- popup_manager::{MountPopupParams, PopupHandle},
+ popup_manager::PopupHolder,
steam_utils::{self, AppID, SteamUtils},
},
- views::{self, game_cover, game_launcher},
+ views::{self, ViewTrait, ViewUpdateParams, game_cover},
};
#[derive(Clone)]
enum Task {
AppManifestClicked(steam_utils::AppManifest),
SetCoverArt(AppID, Rc),
- CloseLauncher,
LoadManifests,
FillPage(u32),
PrevPage,
@@ -42,6 +41,7 @@ pub struct Params<'a> {
pub frontend_tasks: FrontendTasks,
pub layout: &'a mut Layout,
pub parent_id: WidgetID,
+ pub steam_utils: &'a SteamUtils,
}
const MAX_GAMES_PER_PAGE: u32 = 30;
@@ -50,10 +50,6 @@ pub struct GameCoverCell {
view_cover: game_cover::View,
}
-struct State {
- view_launcher: Option<(PopupHandle, views::game_launcher::View)>,
-}
-
pub struct View {
#[allow(dead_code)]
parser_state: ParserState,
@@ -63,12 +59,37 @@ pub struct View {
id_list_parent: WidgetID,
game_cover_view_common: game_cover::ViewCommon,
executor: AsyncExecutor,
- state: Rc>,
mounted_game_covers: HashMap,
all_manifests: Vec,
cur_page: u32,
page_count: u32,
id_label_page: WidgetID,
+ view_launcher: PopupHolder,
+ steam_utils: SteamUtils,
+}
+
+impl ViewTrait for View {
+ fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ loop {
+ let tasks = self.tasks.drain();
+ if tasks.is_empty() {
+ break;
+ }
+ for task in tasks {
+ match task {
+ Task::LoadManifests => self.load_manifests(),
+ Task::FillPage(page_idx) => self.fill_page(&mut par.layout, &mut par.executor, page_idx)?,
+ Task::AppManifestClicked(manifest) => self.action_app_manifest_clicked(manifest)?,
+ Task::SetCoverArt(app_id, cover_art) => self.set_cover_art(&mut par.layout, app_id, cover_art),
+ Task::PrevPage => self.page_prev(),
+ Task::NextPage => self.page_next(),
+ }
+ }
+ }
+
+ self.view_launcher.update(par)?;
+ Ok(())
+ }
}
impl View {
@@ -105,46 +126,15 @@ impl View {
id_list_parent: list_parent.id,
mounted_game_covers: HashMap::new(),
game_cover_view_common: game_cover::ViewCommon::new(params.globals.clone()),
- state: Rc::new(RefCell::new(State { view_launcher: None })),
executor: params.executor,
all_manifests: Vec::new(),
cur_page: 0,
page_count: 0,
id_label_page,
+ view_launcher: Default::default(),
+ steam_utils: params.steam_utils.clone(),
})
}
-
- pub fn update(
- &mut self,
- layout: &mut Layout,
- steam_utils: &mut SteamUtils,
- executor: &AsyncExecutor,
- ) -> anyhow::Result<()> {
- loop {
- let tasks = self.tasks.drain();
- if tasks.is_empty() {
- break;
- }
- for task in tasks {
- match task {
- Task::LoadManifests => self.load_manifests(steam_utils),
- Task::FillPage(page_idx) => self.fill_page(layout, executor, page_idx)?,
- Task::AppManifestClicked(manifest) => self.action_app_manifest_clicked(manifest)?,
- Task::SetCoverArt(app_id, cover_art) => self.set_cover_art(layout, app_id, cover_art),
- Task::CloseLauncher => self.state.borrow_mut().view_launcher = None,
- Task::PrevPage => self.page_prev(),
- Task::NextPage => self.page_next(),
- }
- }
- }
-
- let mut state = self.state.borrow_mut();
- if let Some((_, view)) = &mut state.view_launcher {
- view.update(layout)?;
- }
-
- Ok(())
- }
}
fn fill_game_list(
@@ -187,8 +177,11 @@ fn fill_game_list(
}
impl View {
- fn load_manifests(&mut self, steam_utils: &mut SteamUtils) {
- match steam_utils.list_installed_games(steam_utils::GameSortMethod::PlayDateDesc) {
+ fn load_manifests(&mut self) {
+ match self
+ .steam_utils
+ .list_installed_games(steam_utils::GameSortMethod::PlayDateDesc)
+ {
Ok(manifests) => {
self.page_count = (manifests.len() as u32 + MAX_GAMES_PER_PAGE) / MAX_GAMES_PER_PAGE;
self.all_manifests = manifests;
@@ -233,16 +226,14 @@ impl View {
}
// set page text
- let mut c = layout.start_common();
{
- let mut common = c.common();
- let mut widget = common.state.widgets.cast_as::(self.id_label_page)?;
+ let mut c = layout.common();
+ let mut widget = c.state.widgets.cast_as::(self.id_label_page)?;
widget.set_text(
- &mut common,
+ &mut c,
Translation::from_raw_text_string(format!("{}/{}", self.cur_page + 1, self.page_count)),
);
}
- c.finish()?;
fill_game_list(
&mut ConstructEssentials {
@@ -283,36 +274,13 @@ impl View {
}
fn action_app_manifest_clicked(&mut self, manifest: steam_utils::AppManifest) -> anyhow::Result<()> {
- self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
- title: Translation::from_raw_text(&manifest.name),
- on_content: {
- let state = self.state.clone();
- let tasks = self.tasks.clone();
- let executor = self.executor.clone();
- let globals = self.globals.clone();
- let frontend_tasks = self.frontend_tasks.clone();
-
- Rc::new(move |data| {
- let on_launched = {
- let tasks = tasks.clone();
- Box::new(move || tasks.push(Task::CloseLauncher))
- };
-
- let view = game_launcher::View::new(game_launcher::Params {
- manifest: manifest.clone(),
- executor: executor.clone(),
- globals: &globals,
- layout: data.layout,
- parent_id: data.id_content,
- frontend_tasks: &frontend_tasks,
- on_launched,
- })?;
-
- state.borrow_mut().view_launcher = Some((data.handle, view));
- Ok(())
- })
- },
- }));
+ views::game_launcher::mount_popup(
+ self.frontend_tasks.clone(),
+ self.executor.clone(),
+ self.globals.clone(),
+ manifest,
+ self.view_launcher.clone(),
+ );
Ok(())
}
diff --git a/dash-frontend/src/views/mod.rs b/dash-frontend/src/views/mod.rs
index d2cd9369..918c67fc 100644
--- a/dash-frontend/src/views/mod.rs
+++ b/dash-frontend/src/views/mod.rs
@@ -1,6 +1,25 @@
+use wlx_common::{async_executor::AsyncExecutor, config::GeneralConfig, dash_interface::ConfigChangeKind};
+
pub mod app_launcher;
pub mod audio_settings;
+pub mod dialog_box;
+pub mod download_file;
pub mod game_cover;
pub mod game_launcher;
pub mod game_list;
+pub mod remote_skymap_downloader;
+pub mod remote_skymap_list;
pub mod running_games_list;
+pub mod skymap_list;
+pub mod skymap_list_cell;
+
+pub struct ViewUpdateParams<'a> {
+ pub layout: &'a mut wgui::layout::Layout,
+ pub executor: &'a AsyncExecutor,
+ pub general_config: &'a mut GeneralConfig,
+ pub config_change_kind: &'a mut Option,
+}
+
+pub trait ViewTrait {
+ fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()>;
+}
diff --git a/dash-frontend/src/views/remote_skymap_downloader.rs b/dash-frontend/src/views/remote_skymap_downloader.rs
new file mode 100644
index 00000000..4a2aa537
--- /dev/null
+++ b/dash-frontend/src/views/remote_skymap_downloader.rs
@@ -0,0 +1,396 @@
+use std::{collections::HashMap, rc::Rc};
+
+use crate::{
+ frontend::{FrontendTask, FrontendTasks},
+ util::{
+ networking::{self, skymap_catalog::SkymapResolution},
+ popup_manager::{MountPopupOnceParams, PopupHolder},
+ },
+ views::{self, ViewTrait, ViewUpdateParams},
+};
+use anyhow::Context;
+use wgui::{
+ assets::AssetPath,
+ components::button::ComponentButton,
+ drawing::Color,
+ globals::WguiGlobals,
+ i18n::Translation,
+ layout::{Layout, WidgetID},
+ parser::{Fetchable, ParseDocumentParams, ParserState},
+ renderer_vk::text::custom_glyph::CustomGlyphData,
+ task::Tasks,
+ widget::{image::WidgetImage, label::WidgetLabel},
+};
+use wlx_common::{async_executor::AsyncExecutor, config_io, dash_interface::ConfigChangeKind};
+
+pub struct Params<'a> {
+ pub globals: &'a WguiGlobals,
+ pub layout: &'a mut Layout,
+ pub executor: &'a AsyncExecutor,
+ pub frontend_tasks: FrontendTasks,
+ pub parent_id: WidgetID,
+ pub entry: networking::skymap_catalog::SkymapCatalogEntry,
+ pub preview_image: CustomGlyphData,
+ pub preview_image_compressed: Rc>,
+ pub on_updated_library: Rc,
+}
+
+#[derive(Clone)]
+enum Task {
+ Refresh,
+ ResolutionClicked(SkymapResolution),
+ DownloadFinished,
+ RunDownload(SkymapResolution),
+ RemoveFile(SkymapResolution),
+ SetSkymap(SkymapResolution),
+}
+
+pub struct View {
+ entry: networking::skymap_catalog::SkymapCatalogEntry,
+ frontend_tasks: FrontendTasks,
+ globals: WguiGlobals,
+ tasks: Tasks,
+ executor: AsyncExecutor,
+
+ id_resolution_buttons: WidgetID,
+
+ #[allow(dead_code)]
+ parser_state: ParserState,
+
+ popup_download: PopupHolder,
+ popup_dialog_box: PopupHolder,
+
+ preview_image_compressed: Rc>,
+ on_updated_library: Rc,
+}
+
+fn mount_resolution_button(
+ layout: &mut Layout,
+ parser_state: &mut ParserState,
+ doc_params: &ParseDocumentParams,
+ parent_id: WidgetID,
+ res: SkymapResolution,
+ tasks: &Tasks,
+ already_downloaded: bool,
+) -> anyhow::Result<()> {
+ let mut t = HashMap::, Rc>::new();
+ t.insert(Rc::from("text"), Rc::from(res.get_display_str()));
+ t.insert(
+ Rc::from("sprite"),
+ Rc::from(match already_downloaded {
+ true => "dashboard/check.svg",
+ false => "dashboard/download.svg",
+ }),
+ );
+ let data = parser_state.realize_template(doc_params, "ResolutionButton", layout, parent_id, t)?;
+ let button = data.fetch_component_as::("button")?;
+
+ if already_downloaded {
+ button.set_color(&mut layout.common(), Color::new(0.0, 0.4, 0.0, 1.0)); // green
+ }
+ tasks.handle_button(&button, Task::ResolutionClicked(res));
+ Ok(())
+}
+
+impl ViewTrait for View {
+ fn update(&mut self, par: &mut ViewUpdateParams) -> anyhow::Result<()> {
+ for task in self.tasks.drain() {
+ match task {
+ Task::ResolutionClicked(resolution) => {
+ self.resolution_clicked(resolution)?;
+ }
+ Task::Refresh => {
+ self.refresh(par.layout)?;
+ }
+ Task::DownloadFinished => {
+ self.download_finished()?;
+ }
+ Task::RunDownload(resolution) => {
+ self.run_download(resolution)?;
+ }
+ Task::RemoveFile(resolution) => {
+ self.remove_file(resolution)?;
+ }
+ Task::SetSkymap(resolution) => {
+ let skymap_file_path = self
+ .entry
+ .get_destination_path(resolution)
+ .context("Skymap not found" /* you shouldn't really see this, like ever. */)?;
+
+ par.general_config.skybox_texture = skymap_file_path
+ .to_str()
+ .context("Skymap filename not valid UTF-8")?
+ .into();
+ *par.config_change_kind = Some(ConfigChangeKind::EnvironmentBlend);
+ }
+ }
+ }
+
+ self.popup_download.update(par)?;
+ self.popup_dialog_box.update(par)?;
+ Ok(())
+ }
+}
+
+fn doc_params(globals: &WguiGlobals) -> ParseDocumentParams<'_> {
+ ParseDocumentParams {
+ globals: globals.clone(),
+ path: AssetPath::BuiltIn("gui/view/remote_skymap_downloader.xml"),
+ extra: Default::default(),
+ }
+}
+
+impl View {
+ pub fn new(par: Params) -> anyhow::Result {
+ let tasks = Tasks::::new();
+
+ let parser_state = wgui::parser::parse_from_assets(&doc_params(&par.globals), par.layout, par.parent_id)?;
+ let id_resolution_buttons = parser_state.get_widget_id("resolution_buttons")?;
+
+ let str_version = par.globals.i18n().translate("VERSION");
+ let str_creation_date = par.globals.i18n().translate("CREATION_DATE");
+ let str_modification_date = par.globals.i18n().translate("MODIFICATION_DATE");
+
+ let image = parser_state.fetch_widget(&par.layout.state, "image")?.widget;
+ let mut image = image.cast::()?;
+ image.set_content(&mut par.layout.alterables, Some(par.preview_image));
+
+ // Set author label
+ parser_state
+ .fetch_widget_as::(&par.layout.state, "label_author")?
+ .set_text_simple(
+ &mut par.globals.get(),
+ Translation::from_raw_text_string(format!("by {}", par.entry.author)),
+ );
+
+ // Set description label
+ parser_state
+ .fetch_widget_as::