diff --git a/.gitattributes b/.gitattributes index b5348bf23..8abb5d87b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,7 @@ * text=auto eol=lf Cargo.lock linguist-generated=false *.png filter=lfs diff=lfs merge=lfs -text +*.gif filter=lfs diff=lfs merge=lfs -text # Exclude some small files from LFS: crates/eframe/data/* !filter !diff !merge text=auto eol=lf diff --git a/.github/workflows/cargo_machete.yml b/.github/workflows/cargo_machete.yml index 2b06dfdef..b6f472504 100644 --- a/.github/workflows/cargo_machete.yml +++ b/.github/workflows/cargo_machete.yml @@ -6,7 +6,13 @@ jobs: cargo-machete: runs-on: ubuntu-latest steps: + - uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.85 + - name: Machete install + run: cargo install cargo-machete --locked - name: Checkout - uses: actions/checkout@v3 - - name: Machete - run: cargo install cargo-machete --locked && cargo machete + uses: actions/checkout@v4 + - name: Machete Check + run: cargo machete + diff --git a/.github/workflows/deploy_web_demo.yml b/.github/workflows/deploy_web_demo.yml index 6eb546961..6c9d80962 100644 --- a/.github/workflows/deploy_web_demo.yml +++ b/.github/workflows/deploy_web_demo.yml @@ -39,7 +39,7 @@ jobs: with: profile: minimal target: wasm32-unknown-unknown - toolchain: 1.80.0 + toolchain: 1.81.0 override: true - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/png_only_on_lfs.yml b/.github/workflows/png_only_on_lfs.yml index 624a7f450..7e006e755 100644 --- a/.github/workflows/png_only_on_lfs.yml +++ b/.github/workflows/png_only_on_lfs.yml @@ -25,7 +25,7 @@ jobs: exclude_pattern=$(printf "|^%s" "${exclude_paths[@]}" | sed 's/^|//') if comm -23 <(git ls-files | grep -Ev "$exclude_pattern" | sort) <(git lfs ls-files -n | sort) | grep "\.${ext}$"; then - echo "Error: Found binary file with extension .$ext not tracked by git LFS. See CONTRIBUTING.md" + echo "Error: Found binary file with extension .$ext not tracked by git LFS. See https://github.com/emilk/egui/blob/master/CONTRIBUTING.md#working-with-git-lfs" exit 1 fi done diff --git a/.github/workflows/preview_build.yml b/.github/workflows/preview_build.yml index c44c10f0c..932569c48 100644 --- a/.github/workflows/preview_build.yml +++ b/.github/workflows/preview_build.yml @@ -16,8 +16,11 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - run: rustup toolchain install stable --profile minimal --target wasm32-unknown-unknown + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.81.0 + targets: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v2 with: prefix-key: "pr-preview-" diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index b1f5a5a37..eadce83be 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -9,7 +9,7 @@ env: jobs: fmt-crank-check-test: - name: Format + check + test + name: Format + check runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 @@ -18,11 +18,11 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.80.0 + toolchain: 1.81.0 - name: Install packages (Linux) if: runner.os == 'Linux' - uses: awalsh128/cache-apt-pkgs-action@v1.4.2 + uses: awalsh128/cache-apt-pkgs-action@v1.4.3 with: packages: libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libssl-dev libgtk-3-dev # libgtk-3-dev is used by rfd version: 1.0 @@ -83,7 +83,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.80.0 + toolchain: 1.81.0 targets: wasm32-unknown-unknown - run: sudo apt-get update && sudo apt-get install libgtk-3-dev libatk1.0-dev @@ -103,7 +103,7 @@ jobs: - name: wasm-bindgen uses: jetli/wasm-bindgen-action@v0.1.0 with: - version: "0.2.95" + version: "0.2.97" - run: ./scripts/wasm_bindgen_check.sh --skip-setup @@ -153,9 +153,9 @@ jobs: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - - uses: EmbarkStudios/cargo-deny-action@v1 + - uses: EmbarkStudios/cargo-deny-action@v2 with: - rust-version: "1.80.0" + rust-version: "1.81.0" log-level: error command: check arguments: --target ${{ matrix.target }} @@ -170,7 +170,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.80.0 + toolchain: 1.81.0 targets: aarch64-linux-android - name: Set up cargo cache @@ -189,7 +189,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.80.0 + toolchain: 1.81.0 targets: aarch64-apple-ios - name: Set up cargo cache @@ -208,7 +208,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.80.0 + toolchain: 1.81.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 @@ -223,7 +223,7 @@ jobs: tests: name: Run tests - # We run the tests on macOS because it will run with a actual GPU + # We run the tests on macOS because it will run with an actual GPU runs-on: macos-latest steps: @@ -232,7 +232,7 @@ jobs: lfs: true - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.80.0 + toolchain: 1.81.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/spelling_and_links.yml b/.github/workflows/spelling_and_links.yml index 2b4c8de14..d7b32b007 100644 --- a/.github/workflows/spelling_and_links.yml +++ b/.github/workflows/spelling_and_links.yml @@ -4,7 +4,7 @@ on: [pull_request] jobs: typos: # https://github.com/crate-ci/typos - # Add exceptions to _typos.toml + # Add exceptions to .typos.toml # install and run locally: cargo install typos-cli && typos name: typos runs-on: ubuntu-latest @@ -14,15 +14,7 @@ jobs: - name: Check spelling of entire workspace uses: crate-ci/typos@master - # Disabled: too many names of crates and user-names etc - # spellcheck: - # name: Spellcheck - # runs-on: ubuntu-latest - # steps: - # - uses: actions/checkout@v4 - # - uses: streetsidesoftware/cspell-action@v2 - # with: - # files: "**/*.md" + linkinator: name: linkinator runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index 887de9da3..588826e7d 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ **/target_wasm **/tests/snapshots/**/*.diff.png **/tests/snapshots/**/*.new.png +**/tests/snapshots/**/*.old.png /.*.json /.vscode /media/* diff --git a/.typos.toml b/.typos.toml index de51a691c..b9d882beb 100644 --- a/.typos.toml +++ b/.typos.toml @@ -6,6 +6,17 @@ ime = "ime" # Input Method Editor nknown = "nknown" # part of @55nknown username ro = "ro" # read-only, also part of the username @Phen-Ro +typ = "typ" # Often used because `type` is a keyword in Rust + +# I mistype these so often +tesalator = "tessellator" +teselator = "tessellator" +tessalator = "tessellator" +tesselator = "tessellator" +tesalation = "tessellation" +teselation = "tessellation" +tessalation = "tessellation" +tesselation = "tessellation" [files] extend-exclude = ["web_demo/egui_demo_app.js"] # auto-generated diff --git a/.vscode/settings.json b/.vscode/settings.json index d4794e033..677cb3c4e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -33,7 +33,11 @@ "--all-features", ], "rust-analyzer.showUnlinkedFileNotification": false, - + "rust-analyzer.cargo.extraEnv": { + // rust-analyzer is only guaranteed to support the latest stable version of Rust. Use it instead of whatever is + // specified in rust-toolchain. + "RUSTUP_TOOLCHAIN": "stable" + }, // Uncomment the following options and restart rust-analyzer to get it to check code behind `cfg(target_arch=wasm32)`. // Don't forget to put it in a comment again before committing. // "rust-analyzer.cargo.target": "wasm32-unknown-unknown", diff --git a/CHANGELOG.md b/CHANGELOG.md index b6733405c..7ef679124 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,93 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.1 - 2025-03-05 +* Fix sizing bug in `TextEdit::singleline` [#5640](https://github.com/emilk/egui/pull/5640) by [@IaVashik](https://github.com/IaVashik) +* Fix panic when rendering thin textured rectangles [#5692](https://github.com/emilk/egui/pull/5692) by [@PPakalns](https://github.com/PPakalns) + + +## 0.31.0 - 2025-02-04 - Scene container, improved rendering quality + +### Highlights ✨ + +#### Scene container +This release adds the `Scene` container to egui. It is a pannable, zoomable canvas that can contain `Widget`s and child `Ui`s. +This will make it easier to e.g. implement a graph editor. + +![scene](https://github.com/user-attachments/assets/7dc5e395-a3cb-4bf3-83a3-51a76a48c409) + +#### Clearer, pixel perfect rendering +The tessellator has been updated for improved rendering quality and better performance. It will produce fewer vertices +and shapes will have less overdraw. We've also defined what `CornerRadius` (previously `Rounding`) means. + +We've also added a tessellator test to the [demo app](https://www.egui.rs/), where you can play around with different +values to see what's produced: + + +https://github.com/user-attachments/assets/adf55e3b-fb48-4df0-aaa2-150ee3163684 + + +Check the [PR](https://github.com/emilk/egui/pull/5669) for more details. + +#### `CornerRadius`, `Margin`, `Shadow` size reduction +In order to pave the path for more complex and customizable styling solutions, we've reduced the size of +`CornerRadius`, `Margin` and `Shadow` values to `i8` and `u8`. + + + +### Migration guide +- Add a `StrokeKind` to all your `Painter::rect` calls [#5648](https://github.com/emilk/egui/pull/5648) +- `StrokeKind::default` was removed, since the 'normal' value depends on the context [#5658](https://github.com/emilk/egui/pull/5658) + - You probably want to use `StrokeKind::Inside` when drawing rectangles + - You probably want to use `StrokeKind::Middle` when drawing open paths +- Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673) +- `CornerRadius`, `Margin` and `Shadow` have been updated to use `i8` and `u8` [#5563](https://github.com/emilk/egui/pull/5563), [#5567](https://github.com/emilk/egui/pull/5567), [#5568](https://github.com/emilk/egui/pull/5568) + - Remove the .0 from your values + - Cast dynamic values with `as i8` / `as u8` or `as _` if you want Rust to infer the type + - Rust will do a 'saturating' cast, so if your `f32` value is bigger than `127` it will be clamped to `127` +- `RectShape` parameters changed [#5565](https://github.com/emilk/egui/pull/5565) + - Prefer to use the builder methods to create it instead of initializing it directly +- `Frame` now takes the `Stroke` width into account for its sizing, so check all views of your app to make sure they still look right. + Read the [PR](https://github.com/emilk/egui/pull/5575) for more info. + +### ⭐ Added +* Add `egui::Scene` for panning/zooming a `Ui` [#5505](https://github.com/emilk/egui/pull/5505) by [@grtlr](https://github.com/grtlr) +* Animated WebP support [#5470](https://github.com/emilk/egui/pull/5470) by [@Aely0](https://github.com/Aely0) +* Improve tessellation quality [#5669](https://github.com/emilk/egui/pull/5669) by [@emilk](https://github.com/emilk) +* Add `OutputCommand` for copying text and opening URL:s [#5532](https://github.com/emilk/egui/pull/5532) by [@emilk](https://github.com/emilk) +* Add `Context::copy_image` [#5533](https://github.com/emilk/egui/pull/5533) by [@emilk](https://github.com/emilk) +* Add `WidgetType::Image` and `Image::alt_text` [#5534](https://github.com/emilk/egui/pull/5534) by [@lucasmerlin](https://github.com/lucasmerlin) +* Add `epaint::Brush` for controlling `RectShape` texturing [#5565](https://github.com/emilk/egui/pull/5565) by [@emilk](https://github.com/emilk) +* Implement `nohash_hasher::IsEnabled` for `Id` [#5628](https://github.com/emilk/egui/pull/5628) by [@emilk](https://github.com/emilk) +* Add keys for `!`, `{`, `}` [#5548](https://github.com/emilk/egui/pull/5548) by [@Its-Just-Nans](https://github.com/Its-Just-Nans) +* Add `RectShape::stroke_kind ` to control if stroke is inside/outside/centered [#5647](https://github.com/emilk/egui/pull/5647) by [@emilk](https://github.com/emilk) + +### πŸ”§ Changed +* ⚠️ `Frame` now includes stroke width as part of padding [#5575](https://github.com/emilk/egui/pull/5575) by [@emilk](https://github.com/emilk) +* Rename `Rounding` to `CornerRadius` [#5673](https://github.com/emilk/egui/pull/5673) by [@emilk](https://github.com/emilk) +* Require a `StrokeKind` when painting rectangles with strokes [#5648](https://github.com/emilk/egui/pull/5648) by [@emilk](https://github.com/emilk) +* Round widget coordinates to even multiple of 1/32 [#5517](https://github.com/emilk/egui/pull/5517) by [@emilk](https://github.com/emilk) +* Make all lines and rectangles crisp [#5518](https://github.com/emilk/egui/pull/5518) by [@emilk](https://github.com/emilk) +* Tweak window resize handles [#5524](https://github.com/emilk/egui/pull/5524) by [@emilk](https://github.com/emilk) + +### πŸ”₯ Removed +* Remove `egui::special_emojis::TWITTER` [#5622](https://github.com/emilk/egui/pull/5622) by [@emilk](https://github.com/emilk) +* Remove `StrokeKind::default` [#5658](https://github.com/emilk/egui/pull/5658) by [@emilk](https://github.com/emilk) + +### πŸ› Fixed +* Use correct minimum version of `profiling` crate [#5494](https://github.com/emilk/egui/pull/5494) by [@lucasmerlin](https://github.com/lucasmerlin) +* Fix interactive widgets sometimes being incorrectly marked as hovered [#5523](https://github.com/emilk/egui/pull/5523) by [@emilk](https://github.com/emilk) +* Fix panic due to non-total ordering in `Area::compare_order()` [#5569](https://github.com/emilk/egui/pull/5569) by [@HactarCE](https://github.com/HactarCE) +* Fix hovering through custom menu button [#5555](https://github.com/emilk/egui/pull/5555) by [@M4tthewDE](https://github.com/M4tthewDE) + +### πŸš€ Performance +* Use `u8` in `CornerRadius`, and introduce `CornerRadiusF32` [#5563](https://github.com/emilk/egui/pull/5563) by [@emilk](https://github.com/emilk) +* Store `Margin` using `i8` to reduce its size [#5567](https://github.com/emilk/egui/pull/5567) by [@emilk](https://github.com/emilk) +* Shrink size of `Shadow` by using `i8/u8` instead of `f32` [#5568](https://github.com/emilk/egui/pull/5568) by [@emilk](https://github.com/emilk) +* Avoid allocations for loader cache lookup [#5584](https://github.com/emilk/egui/pull/5584) by [@mineichen](https://github.com/mineichen) +* Use bitfield instead of bools in `Response` and `Sense` [#5556](https://github.com/emilk/egui/pull/5556) by [@polwel](https://github.com/polwel) + + ## 0.30.0 - 2024-12-16 - Modals and better layer support ### ✨ Highlights diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 48c9e9ae5..613de20c5 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,14 +34,11 @@ Browse through [`ARCHITECTURE.md`](ARCHITECTURE.md) to get a sense of how all pi You can test your code locally by running `./scripts/check.sh`. There are snapshots test that might need to be updated. Run the tests with `UPDATE_SNAPSHOTS=true cargo test --workspace --all-features` to update all of them. +If CI keeps complaining about snapshots (which could happen if you don't use macOS, snapshots in CI are currently +rendered with macOS), you can instead run `./scripts/update_snapshots_from_ci.sh` to update your local snapshots from +the last CI run of your PR (which will download the `test_results` artefact). For more info about the tests see [egui_kittest](./crates/egui_kittest/README.md). - -We use [git-lfs](https://git-lfs.com/) to store big files in the repository. -Make sure you have it installed (running `git lfs ls-files` from the repository root should list some files). -Don't forget to run `git lfs install` after installing the git-lfs binary. -You need to add any .png images to `git lfs`. -If the CI complains about this, make sure you run `git add --renormalize .`. - +Snapshots and other big files are stored with git lfs. See [Working with git lfs](#working-with-git-lfs) for more info. If you see an `InvalidSignature` error when running snapshot tests, it's probably a problem related to git-lfs. When you have something that works, open a draft PR. You may get some helpful feedback early! @@ -51,6 +48,31 @@ Don't worry about having many small commits in the PR - they will be squashed to Please keep pull requests small and focused. The smaller it is, the more likely it is to get merged. +## Working with git lfs + +We use [git-lfs](https://git-lfs.com/) to store big files in the repository. +Make sure you have it installed (running `git lfs ls-files` from the repository root should list some files). +Don't forget to run `git lfs install` in this repo after installing the git-lfs binary. +You need to add any .png images to `git lfs` (see the .gitattributes file for rules and exclusions). +If the CI complains about lfs, try running `git add --renormalize .`. + +Common git-lfs commands: +```bash +# Install git-lfs in the repo (installs git hooks) +git lfs install + +# Move a file to git lfs +git lfs track "path/to/file/or/pattern" # OR manually edit .gitattributes +git add --renormalize . # Moves already added files to lfs (according to .gitattributes) + +# Move a file from lfs to regular git +git lfs untrack "path/to/file/or/pattern" # OR manually edit .gitattributes +git add --renormalize . # Moves already added files to regular git (according to .gitattributes) + +# Push to a contributor remote (see https://github.com/cli/cli/discussions/8794#discussioncomment-8695076) +git push --no-verify +``` + ## PR review Most PR reviews are done by me, Emil, but I very much appreciate any help I can get reviewing PRs! diff --git a/Cargo.lock b/Cargo.lock index 313565515..038973ccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -20,57 +20,72 @@ checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" [[package]] name = "accesskit" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c97bb3cc1dacbdc6d1147040fc61309590d3e1ab5efd92a8a09c7a2e07284c" +checksum = "becf0eb5215b6ecb0a739c31c21bd83c4f326524c9b46b7e882d77559b60a529" dependencies = [ "enumn", "serde", ] [[package]] -name = "accesskit_atspi_common" -version = "0.10.0" +name = "accesskit_android" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03db49d2948db6875c69a1ef17816efa8e3d9f36c7cd79e467d8562a6695662b" +checksum = "d42f298f1db7c022cc69f20f06085b34b08ffae79b37488b7aae20b210777d17" +dependencies = [ + "accesskit", + "accesskit_consumer", + "jni", + "log", + "once_cell", + "paste", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce9928251cd5651ae983a77aeaa528471eed47cf705885e0b03249b72fe4e8e1" dependencies = [ "accesskit", "accesskit_consumer", "atspi-common", "serde", - "thiserror", - "zvariant 4.2.0", + "thiserror 1.0.66", + "zvariant", ] [[package]] name = "accesskit_consumer" -version = "0.25.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa3a17950ce0d911f132387777b9b3d05eddafb59b773ccaa53fceefaeb0228e" +checksum = "d0bf66a7bf0b7ea4fd7742d50b64782a88f99217cf246b3f93b4162528dde520" dependencies = [ "accesskit", + "hashbrown", "immutable-chunkmap", ] [[package]] name = "accesskit_macos" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8d94b7544775dddce398e2500a8b3cc2be3655190879071ce6a9e5610195be4" +checksum = "09e230718177753b4e4ad9e1d9f6cfc2f4921212d4c1c480b253f526babb258d" dependencies = [ "accesskit", "accesskit_consumer", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "once_cell", + "hashbrown", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", ] [[package]] name = "accesskit_unix" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a88d913b144104dd825f75db1b82c63d754b01c53c2f9b7545dcdfae63bb0ed" +checksum = "2ef06642e9f02f1708ad55e1eaeb8ad6956c22917699c4f313afa4f8f1b5e664" dependencies = [ "accesskit", "accesskit_atspi_common", @@ -81,17 +96,18 @@ dependencies = [ "futures-lite", "futures-util", "serde", - "zbus 4.4.0", + "zbus", ] [[package]] name = "accesskit_windows" -version = "0.24.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aaa870a5d047338f03707706141f22c98c20e79d5403bf3c9b195549e6cdeea" +checksum = "65178f3df98a51e4238e584fcb255cb1a4f9111820848eeddd37663be40a625f" dependencies = [ "accesskit", "accesskit_consumer", + "hashbrown", "paste", "static_assertions", "windows", @@ -100,11 +116,12 @@ dependencies = [ [[package]] name = "accesskit_winit" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3555a67a9bb208f620cfc3746f1502d1512f0ffbdb19c6901aa90b111aa56ec5" +checksum = "2c28531b0a1612b46d057a724a1e3de42a4bb101ff9f18c96c32f605b6e5ef06" dependencies = [ "accesskit", + "accesskit_android", "accesskit_macos", "accesskit_unix", "accesskit_windows", @@ -134,11 +151,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "serde", "version_check", - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -150,12 +167,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" - [[package]] name = "android-activity" version = "0.6.0" @@ -163,7 +174,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.6.0", + "bitflags 2.8.0", "cc", "cesu8", "jni", @@ -174,7 +185,7 @@ dependencies = [ "ndk-context", "ndk-sys 0.6.0+11769913", "num_enum", - "thiserror", + "thiserror 1.0.66", ] [[package]] @@ -243,9 +254,9 @@ dependencies = [ "core-graphics", "image", "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "parking_lot", "windows-sys 0.48.0", "x11rb", @@ -280,9 +291,9 @@ dependencies = [ [[package]] name = "ashpd" -version = "0.10.2" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c39d707614dbcc6bed00015539f488d8e3fe3e66ed60961efc0c90f4b380b3" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" dependencies = [ "async-fs", "async-net", @@ -294,10 +305,7 @@ dependencies = [ "serde", "serde_repr", "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "zbus 5.1.1", + "zbus", ] [[package]] @@ -462,9 +470,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "atspi" -version = "0.22.0" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf" +checksum = "c83247582e7508838caf5f316c00791eee0e15c0bf743e6880585b867e16815c" dependencies = [ "atspi-common", "atspi-connection", @@ -473,42 +481,41 @@ dependencies = [ [[package]] name = "atspi-common" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7" +checksum = "33dfc05e7cdf90988a197803bf24f5788f94f7c94a69efa95683e8ffe76cfdfb" dependencies = [ "enumflags2", "serde", "static_assertions", - "zbus 4.4.0", + "zbus", "zbus-lockstep", "zbus-lockstep-macros", - "zbus_names 3.0.0", - "zvariant 4.2.0", + "zbus_names", + "zvariant", ] [[package]] name = "atspi-connection" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333" +checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938" dependencies = [ "atspi-common", "atspi-proxies", "futures-lite", - "zbus 4.4.0", + "zbus", ] [[package]] name = "atspi-proxies" -version = "0.6.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" +checksum = "d2eebcb9e7e76f26d0bcfd6f0295e1cd1e6f33bedbc5698a971db8dc43d7751c" dependencies = [ "atspi-common", "serde", - "zbus 4.4.0", - "zvariant 4.2.0", + "zbus", ] [[package]] @@ -591,9 +598,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -604,22 +611,22 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "block2" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +dependencies = [ + "objc2 0.6.0", ] [[package]] @@ -643,9 +650,9 @@ checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "bytemuck" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8334215b81e418a0a7bdb8ef0849474f40bb10c8b71f1c4ed315cff49f32494d" +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" dependencies = [ "bytemuck_derive", ] @@ -685,12 +692,12 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "log", "polling", "rustix", "slab", - "thiserror", + "thiserror 1.0.66", ] [[package]] @@ -713,9 +720,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.1.31" +version = "1.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e7962b54006dcfcc61cb72735f4d89bb97061dd6a7ed882ec6b8ee53714c6f" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" dependencies = [ "jobserver", "libc", @@ -734,12 +741,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -935,15 +936,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cpufeatures" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" -dependencies = [ - "libc", -] - [[package]] name = "crc32fast" version = "1.4.2" @@ -1027,16 +1019,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "cursor-icon" version = "1.1.0" @@ -1123,16 +1105,6 @@ dependencies = [ "rayon", ] -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "directories" version = "5.0.1" @@ -1160,6 +1132,18 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" +[[package]] +name = "dispatch2" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a0d569e003ff27784e0e14e4a594048698e0c0f0b66cabcb51511be55a7caa0" +dependencies = [ + "bitflags 2.8.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1203,7 +1187,7 @@ checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" [[package]] name = "ecolor" -version = "0.30.0" +version = "0.31.1" dependencies = [ "bytemuck", "cint", @@ -1215,7 +1199,7 @@ dependencies = [ [[package]] name = "eframe" -version = "0.30.0" +version = "0.31.1" dependencies = [ "ahash", "bytemuck", @@ -1225,19 +1209,19 @@ dependencies = [ "egui-wgpu", "egui-winit", "egui_glow", - "glow 0.16.0", + "glow", "glutin", "glutin-winit", "home", "image", "js-sys", "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "parking_lot", "percent-encoding", - "pollster 0.4.0", + "pollster", "profiling", "raw-window-handle 0.6.2", "ron", @@ -1255,11 +1239,12 @@ dependencies = [ [[package]] name = "egui" -version = "0.30.0" +version = "0.31.1" dependencies = [ "accesskit", "ahash", "backtrace", + "bitflags 2.8.0", "document-features", "emath", "epaint", @@ -1272,7 +1257,7 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.30.0" +version = "0.31.1" dependencies = [ "ahash", "bytemuck", @@ -1281,7 +1266,7 @@ dependencies = [ "epaint", "log", "profiling", - "thiserror", + "thiserror 1.0.66", "type-map", "web-time", "wgpu", @@ -1290,7 +1275,7 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.30.0" +version = "0.31.1" dependencies = [ "accesskit_winit", "ahash", @@ -1311,7 +1296,7 @@ dependencies = [ [[package]] name = "egui_demo_app" -version = "0.30.0" +version = "0.31.1" dependencies = [ "bytemuck", "chrono", @@ -1338,7 +1323,7 @@ dependencies = [ [[package]] name = "egui_demo_lib" -version = "0.30.0" +version = "0.31.1" dependencies = [ "chrono", "criterion", @@ -1349,12 +1334,11 @@ dependencies = [ "rand", "serde", "unicode_names2", - "wgpu", ] [[package]] name = "egui_extras" -version = "0.30.0" +version = "0.31.1" dependencies = [ "ahash", "chrono", @@ -1373,14 +1357,14 @@ dependencies = [ [[package]] name = "egui_glow" -version = "0.30.0" +version = "0.31.1" dependencies = [ "ahash", "bytemuck", "document-features", "egui", "egui-winit", - "glow 0.16.0", + "glow", "glutin", "glutin-winit", "log", @@ -1393,16 +1377,17 @@ dependencies = [ [[package]] name = "egui_kittest" -version = "0.30.0" +version = "0.31.1" dependencies = [ "dify", "document-features", "eframe", "egui", "egui-wgpu", + "egui_extras", "image", "kittest", - "pollster 0.4.0", + "pollster", "wgpu", ] @@ -1428,7 +1413,7 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" [[package]] name = "emath" -version = "0.30.0" +version = "0.31.1" dependencies = [ "bytemuck", "document-features", @@ -1519,7 +1504,7 @@ dependencies = [ [[package]] name = "epaint" -version = "0.30.0" +version = "0.31.1" dependencies = [ "ab_glyph", "ahash", @@ -1540,7 +1525,7 @@ dependencies = [ [[package]] name = "epaint_default_fonts" -version = "0.30.0" +version = "0.31.1" [[package]] name = "equivalent" @@ -1641,6 +1626,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1700,9 +1691,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.4.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1fa2f9765705486b33fd2acf1577f8ec449c2ba1f318ae5447697b7c08d210" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ "fastrand", "futures-core", @@ -1722,12 +1713,6 @@ dependencies = [ "syn", ] -[[package]] -name = "futures-sink" -version = "0.3.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" - [[package]] name = "futures-task" version = "0.3.31" @@ -1743,7 +1728,6 @@ dependencies = [ "futures-core", "futures-io", "futures-macro", - "futures-sink", "futures-task", "memchr", "pin-project-lite", @@ -1751,16 +1735,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "gethostname" version = "0.4.3" @@ -1788,7 +1762,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", ] [[package]] @@ -1818,18 +1804,6 @@ dependencies = [ "xml-rs", ] -[[package]] -name = "glow" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51fa363f025f5c111e03f13eda21162faeacb6911fe8caa0c0349f9cf0c4483" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "glow" version = "0.16.0" @@ -1848,8 +1822,8 @@ version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec69412a0bf07ea7607e638b415447857a808846c2b685a43c8aa18bc6d5e499" dependencies = [ - "bitflags 2.6.0", - "cfg_aliases 0.2.1", + "bitflags 2.8.0", + "cfg_aliases", "cgl", "core-foundation", "dispatch", @@ -1857,9 +1831,9 @@ dependencies = [ "glutin_glx_sys", "glutin_wgl_sys", "libloading", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "once_cell", "raw-window-handle 0.6.2", "wayland-sys", @@ -1873,7 +1847,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases", "glutin", "raw-window-handle 0.6.2", "winit", @@ -1914,7 +1888,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "gpu-alloc-types", ] @@ -1924,16 +1898,28 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", +] + +[[package]] +name = "gpu-allocator" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" +dependencies = [ + "log", + "presser", + "thiserror 1.0.66", + "windows", ] [[package]] name = "gpu-descriptor" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c08c1f623a8d0b722b8b99f821eb0ba672a1618f0d3b16ddbee1cedd2dd8557" +checksum = "dcf29e94d6d243368b7a56caa16bc213e4f9f8ed38c4d9557069527b5d5281ca" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "gpu-descriptor-types", "hashbrown", ] @@ -1944,7 +1930,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -1959,20 +1945,26 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", - "allocator-api2", + "foldhash", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hello_android" version = "0.1.0" dependencies = [ "android_logger", "eframe", + "egui_demo_lib", "egui_extras", "log", "winit", @@ -2254,9 +2246,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.5.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -2299,7 +2291,7 @@ dependencies = [ "combine", "jni-sys", "log", - "thiserror", + "thiserror 1.0.66", "walkdir", "windows-sys 0.45.0", ] @@ -2327,10 +2319,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2362,8 +2355,7 @@ checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" [[package]] name = "kittest" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f659954571a3c132356bd15c25f0dcf14d270a28ec5c58797adc2f432831bed5" +source = "git+https://github.com/rerun-io/kittest?branch=main#5803de399c0061d4cd5479929277066faa034331" dependencies = [ "accesskit", "accesskit_consumer", @@ -2398,7 +2390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", - "windows-targets 0.52.6", + "windows-targets 0.48.5", ] [[package]] @@ -2407,7 +2399,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall 0.5.7", ] @@ -2493,11 +2485,11 @@ dependencies = [ [[package]] name = "metal" -version = "0.29.0" +version = "0.31.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" +checksum = "f569fb946490b5743ad69813cb19629130ce9374034abe31614a36402d18f99e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block", "core-graphics-types", "foreign-types", @@ -2548,22 +2540,23 @@ dependencies = [ [[package]] name = "naga" -version = "23.0.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d5941e45a15b53aad4375eedf02033adb7a28931eedc31117faffa52e6a857e" +checksum = "e380993072e52eef724eddfcde0ed013b0c023c3f0417336ed041aa9f076994e" dependencies = [ "arrayvec", "bit-set 0.8.0", - "bitflags 2.6.0", - "cfg_aliases 0.1.1", + "bitflags 2.8.0", + "cfg_aliases", "codespan-reporting", "hexf-parse", "indexmap", "log", "rustc-hash", "spirv", + "strum", "termcolor", - "thiserror", + "thiserror 2.0.11", "unicode-xid", ] @@ -2573,13 +2566,13 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "jni-sys", "log", "ndk-sys 0.6.0+11769913", "num_enum", "raw-window-handle 0.6.2", - "thiserror", + "thiserror 1.0.66", ] [[package]] @@ -2612,9 +2605,9 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", "memoffset", ] @@ -2686,33 +2679,54 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +dependencies = [ + "objc2-encode", +] + [[package]] name = "objc2-app-kit" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", - "block2", + "bitflags 2.8.0", + "block2 0.5.1", "libc", - "objc2", + "objc2 0.5.2", "objc2-core-data", "objc2-core-image", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-quartz-core", ] +[[package]] +name = "objc2-app-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +dependencies = [ + "bitflags 2.8.0", + "block2 0.6.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + [[package]] name = "objc2-cloud-kit" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", + "bitflags 2.8.0", + "block2 0.5.1", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -2721,9 +2735,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -2732,10 +2746,20 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-foundation", + "bitflags 2.8.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +dependencies = [ + "bitflags 2.8.0", + "objc2 0.6.0", ] [[package]] @@ -2744,9 +2768,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -2756,17 +2780,17 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", - "objc2", + "block2 0.5.1", + "objc2 0.5.2", "objc2-contacts", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] name = "objc2-encode" -version = "4.0.3" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" @@ -2774,11 +2798,22 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", - "block2", + "bitflags 2.8.0", + "block2 0.5.1", "dispatch", "libc", - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" +dependencies = [ + "bitflags 2.8.0", + "objc2 0.6.0", + "objc2-core-foundation", ] [[package]] @@ -2787,10 +2822,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -2799,10 +2834,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-foundation", + "bitflags 2.8.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -2811,10 +2846,10 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", - "objc2-foundation", + "bitflags 2.8.0", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] @@ -2824,8 +2859,8 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" dependencies = [ - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -2834,14 +2869,14 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", + "bitflags 2.8.0", + "block2 0.5.1", + "objc2 0.5.2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-image", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-link-presentation", "objc2-quartz-core", "objc2-symbols", @@ -2855,9 +2890,9 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", - "objc2", - "objc2-foundation", + "block2 0.5.1", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -2866,11 +2901,11 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.6.0", - "block2", - "objc2", + "bitflags 2.8.0", + "block2 0.5.1", + "objc2 0.5.2", "objc2-core-location", - "objc2-foundation", + "objc2-foundation 0.2.2", ] [[package]] @@ -2909,6 +2944,15 @@ dependencies = [ "libredox", ] +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -3075,12 +3119,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "pollster" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22686f4785f02a4fcc856d3b3bb19bf6c8160d103f7a99cc258bddd0251dc7f2" - [[package]] name = "pollster" version = "0.4.0" @@ -3089,7 +3127,7 @@ checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" [[package]] name = "popups" -version = "0.30.0" +version = "0.31.1" dependencies = [ "eframe", "env_logger", @@ -3107,9 +3145,15 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + [[package]] name = "proc-macro-crate" version = "3.2.0" @@ -3121,9 +3165,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.89" +version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] @@ -3196,16 +3240,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" -[[package]] -name = "quick-xml" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "quick-xml" version = "0.32.0" @@ -3222,6 +3256,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" dependencies = [ "memchr", + "serde", ] [[package]] @@ -3235,20 +3270,20 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" dependencies = [ - "libc", "rand_chacha", "rand_core", + "zerocopy 0.8.23", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -3256,13 +3291,19 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ - "getrandom", + "getrandom 0.3.1", ] +[[package]] +name = "range-alloc" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8a99fddc9f0ba0a85884b8d14e3592853e787d581ca1816c91349b10e4eeab" + [[package]] name = "raw-window-handle" version = "0.5.2" @@ -3316,7 +3357,7 @@ version = "0.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -3325,9 +3366,9 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.66", ] [[package]] @@ -3381,24 +3422,26 @@ dependencies = [ [[package]] name = "rfd" -version = "0.15.1" +version = "0.15.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f6f80a9b882647d9014673ca9925d30ffc9750f2eed2b4490e189eaebd01e8" +checksum = "80c844748fdc82aae252ee4594a89b6e7ebef1063de7951545564cbc4e57075d" dependencies = [ "ashpd", - "block2", + "block2 0.6.0", + "dispatch2", "js-sys", "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "pollster 0.3.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", + "pollster", "raw-window-handle 0.6.2", "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -3412,15 +3455,14 @@ dependencies = [ [[package]] name = "ring" -version = "0.17.8" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] @@ -3432,7 +3474,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ "base64 0.21.7", - "bitflags 2.6.0", + "bitflags 2.8.0", "serde", "serde_derive", ] @@ -3461,7 +3503,7 @@ version = "0.38.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -3500,6 +3542,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" version = "1.0.18" @@ -3601,17 +3649,6 @@ dependencies = [ "log", ] -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - [[package]] name = "shlex" version = "1.3.0" @@ -3678,7 +3715,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "calloop", "calloop-wayland-source", "cursor-icon", @@ -3686,7 +3723,7 @@ dependencies = [ "log", "memmap2", "rustix", - "thiserror", + "thiserror 1.0.66", "wayland-backend", "wayland-client", "wayland-csd-frame", @@ -3717,19 +3754,13 @@ dependencies = [ "serde", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" - [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -3753,6 +3784,28 @@ dependencies = [ "float-cmp", ] +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3771,9 +3824,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.86" +version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89275301d38033efb81a6e60e3497e734dfcc62571f2854bf4b16690398824c" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", @@ -3808,7 +3861,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror", + "thiserror 1.0.66", "walkdir", "yaml-rust", ] @@ -3882,7 +3935,16 @@ version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.66", +] + +[[package]] +name = "thiserror" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", ] [[package]] @@ -3896,6 +3958,17 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tiff" version = "0.9.1" @@ -3998,7 +4071,7 @@ checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap", "toml_datetime", - "winnow", + "winnow 0.6.20", ] [[package]] @@ -4047,12 +4120,6 @@ dependencies = [ "rustc-hash", ] -[[package]] -name = "typenum" -version = "1.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" - [[package]] name = "uds_windows" version = "1.1.0" @@ -4227,10 +4294,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.95" +name = "wasi" +version = "0.13.3+wasi-0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c" dependencies = [ "cfg-if", "once_cell", @@ -4239,9 +4315,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd" dependencies = [ "bumpalo", "log", @@ -4266,9 +4342,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4276,9 +4352,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d" dependencies = [ "proc-macro2", "quote", @@ -4289,9 +4365,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" +version = "0.2.97" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" +checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49" [[package]] name = "wayland-backend" @@ -4313,7 +4389,7 @@ version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "rustix", "wayland-backend", "wayland-scanner", @@ -4325,7 +4401,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cursor-icon", "wayland-backend", ] @@ -4347,7 +4423,7 @@ version = "0.32.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4359,7 +4435,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4372,7 +4448,7 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4404,9 +4480,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.74" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "a98bc3c33f0fe7e59ad7cd041b89034fa82a7c2d4365ca538dda6cdaf513863c" dependencies = [ "js-sys", "wasm-bindgen", @@ -4456,12 +4532,13 @@ checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" [[package]] name = "wgpu" -version = "23.0.1" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f70000db37c469ea9d67defdc13024ddf9a5f1b89cb2941b812ad7cde1735a" +checksum = "e41253fc7b660735e2a2d9a58c563f2a047d3cc3445293d8f4095538c9e8afbe" dependencies = [ "arrayvec", - "cfg_aliases 0.1.1", + "bitflags 2.8.0", + "cfg_aliases", "document-features", "js-sys", "log", @@ -4481,14 +4558,14 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "23.0.1" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63c3c478de8e7e01786479919c8769f62a22eec16788d8c2ac77ce2c132778a" +checksum = "82a39b8842dc9ffcbe34346e3ab6d496b32a47f6497e119d762c97fcaae3cb37" dependencies = [ "arrayvec", "bit-vec 0.8.0", - "bitflags 2.6.0", - "cfg_aliases 0.1.1", + "bitflags 2.8.0", + "cfg_aliases", "document-features", "indexmap", "log", @@ -4499,28 +4576,30 @@ dependencies = [ "raw-window-handle 0.6.2", "rustc-hash", "smallvec", - "thiserror", + "thiserror 2.0.11", "wgpu-hal", "wgpu-types", ] [[package]] name = "wgpu-hal" -version = "23.0.1" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89364b8a0b211adc7b16aeaf1bd5ad4a919c1154b44c9ce27838213ba05fd821" +checksum = "5a782e5056b060b0b4010881d1decddd059e44f2ecd01e2db2971b48ad3627e5" dependencies = [ "android_system_properties", "arrayvec", "ash", - "bitflags 2.6.0", + "bit-set 0.8.0", + "bitflags 2.8.0", "block", "bytemuck", - "cfg_aliases 0.1.1", + "cfg_aliases", "core-graphics-types", - "glow 0.14.2", + "glow", "glutin_wgl_sys", "gpu-alloc", + "gpu-allocator", "gpu-descriptor", "js-sys", "khronos-egl", @@ -4532,27 +4611,31 @@ dependencies = [ "ndk-sys 0.5.0+25.2.9519653", "objc", "once_cell", + "ordered-float", "parking_lot", "profiling", + "range-alloc", "raw-window-handle 0.6.2", "renderdoc-sys", "rustc-hash", "smallvec", - "thiserror", + "thiserror 2.0.11", "wasm-bindgen", "web-sys", "wgpu-types", "windows", + "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "23.0.0" +version = "24.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "610f6ff27778148c31093f3b03abc4840f9636d58d597ca2f5977433acfe0068" +checksum = "50ac044c0e76c03a0378e7786ac505d010a873665e2d51383dcff8dd227dc69c" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "js-sys", + "log", "web-sys", ] @@ -4578,7 +4661,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -4883,11 +4966,11 @@ dependencies = [ "ahash", "android-activity", "atomic-waker", - "bitflags 2.6.0", - "block2", + "bitflags 2.8.0", + "block2 0.5.1", "bytemuck", "calloop", - "cfg_aliases 0.2.1", + "cfg_aliases", "concurrent-queue", "core-foundation", "core-graphics", @@ -4897,9 +4980,9 @@ dependencies = [ "libc", "memmap2", "ndk", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "objc2-ui-kit", "orbclient", "percent-encoding", @@ -4935,6 +5018,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7f4ea97f6f78012141bcdb6a216b2609f0979ada50b20ca5b52dde2eac2bb1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -5001,7 +5102,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "dlib", "log", "once_cell", @@ -5028,7 +5129,7 @@ checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" [[package]] name = "xtask" -version = "0.30.0" +version = "0.31.1" [[package]] name = "yaml-rust" @@ -5065,9 +5166,9 @@ dependencies = [ [[package]] name = "zbus" -version = "4.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +checksum = "59c333f648ea1b647bc95dc1d34807c8e25ed7a6feff3394034dc4776054b236" dependencies = [ "async-broadcast", "async-executor", @@ -5082,45 +5183,7 @@ dependencies = [ "enumflags2", "event-listener", "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "ordered-stream", - "rand", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros 4.4.0", - "zbus_names 3.0.0", - "zvariant 4.2.0", -] - -[[package]] -name = "zbus" -version = "5.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1162094dc63b1629fcc44150bcceeaa80798cd28bcbe7fa987b65a034c258608" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-util", + "futures-lite", "hex", "nix", "ordered-stream", @@ -5130,74 +5193,50 @@ dependencies = [ "tracing", "uds_windows", "windows-sys 0.59.0", - "winnow", + "winnow 0.7.3", "xdg-home", - "zbus_macros 5.1.1", - "zbus_names 4.1.0", - "zvariant 5.1.0", + "zbus_macros", + "zbus_names", + "zvariant", ] [[package]] name = "zbus-lockstep" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" +checksum = "a22426b1bc2aca91de97772506f0655fa373448e6010d79d5d5880915c388409" dependencies = [ "zbus_xml", - "zvariant 4.2.0", + "zvariant", ] [[package]] name = "zbus-lockstep-macros" -version = "0.4.4" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" +checksum = "100ffec29ed51859052f4563061abe35557acb56ba574510571f8398efc70a29" dependencies = [ "proc-macro2", "quote", "syn", "zbus-lockstep", "zbus_xml", - "zvariant 4.2.0", + "zvariant", ] [[package]] name = "zbus_macros" -version = "4.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +checksum = "f325ad10eb0d0a3eb060203494c3b7ec3162a01a59db75d2deee100339709fc0" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", - "zvariant_utils 2.1.0", -] - -[[package]] -name = "zbus_macros" -version = "5.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cd2dcdce3e2727f7d74b7e33b5a89539b3cc31049562137faf7ae4eb86cd16d" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zbus_names 4.1.0", - "zvariant 5.1.0", - "zvariant_utils 3.0.2", -] - -[[package]] -name = "zbus_names" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant 4.2.0", + "zbus_names", + "zvariant", + "zvariant_utils", ] [[package]] @@ -5208,21 +5247,21 @@ checksum = "856b7a38811f71846fd47856ceee8bccaec8399ff53fb370247e66081ace647b" dependencies = [ "serde", "static_assertions", - "winnow", - "zvariant 5.1.0", + "winnow 0.6.20", + "zvariant", ] [[package]] name = "zbus_xml" -version = "4.0.0" +version = "5.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" +checksum = "589e9a02bfafb9754bb2340a9e3b38f389772684c63d9637e76b1870377bec29" dependencies = [ - "quick-xml 0.30.0", + "quick-xml 0.36.2", "serde", "static_assertions", - "zbus_names 3.0.0", - "zvariant 4.2.0", + "zbus_names", + "zvariant", ] [[package]] @@ -5232,7 +5271,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd97444d05a4328b90e75e503a34bad781f14e28a823ad3557f0750df1ebcbc6" +dependencies = [ + "zerocopy-derive 0.8.23", ] [[package]] @@ -5246,6 +5294,17 @@ dependencies = [ "syn", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6352c01d0edd5db859a63e2605f4ea3183ddbd15e2c4a9e7d32184df75e4f154" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zerofrom" version = "0.1.5" @@ -5312,80 +5371,43 @@ dependencies = [ [[package]] name = "zvariant" -version = "4.2.0" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" -dependencies = [ - "endi", - "enumflags2", - "serde", - "static_assertions", - "zvariant_derive 4.2.0", -] - -[[package]] -name = "zvariant" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1200ee6ac32f1e5a312e455a949a4794855515d34f9909f4a3e082d14e1a56f" +checksum = "b2df9ee044893fcffbdc25de30546edef3e32341466811ca18421e3cd6c5a3ac" dependencies = [ "endi", "enumflags2", "serde", "static_assertions", "url", - "winnow", - "zvariant_derive 5.1.0", - "zvariant_utils 3.0.2", + "winnow 0.7.3", + "zvariant_derive", + "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "4.2.0" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +checksum = "74170caa85b8b84cc4935f2d56a57c7a15ea6185ccdd7eadb57e6edd90f94b2f" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", "syn", - "zvariant_utils 2.1.0", -] - -[[package]] -name = "zvariant_derive" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "687e3b97fae6c9104fbbd36c73d27d149abf04fb874e2efbd84838763daa8916" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn", - "zvariant_utils 3.0.2", + "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "2.1.0" +version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zvariant_utils" -version = "3.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20d1d011a38f12360e5fcccceeff5e2c42a8eb7f27f0dcba97a0862ede05c9c6" +checksum = "e16edfee43e5d7b553b77872d99bc36afdda75c223ca7ad5e3fbecd82ca5fc34" dependencies = [ "proc-macro2", "quote", "serde", "static_assertions", "syn", - "winnow", + "winnow 0.7.3", ] diff --git a/Cargo.toml b/Cargo.toml index 1dcae33b6..92f1ce3c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,8 +23,8 @@ members = [ [workspace.package] edition = "2021" license = "MIT OR Apache-2.0" -rust-version = "1.80" -version = "0.30.0" +rust-version = "1.81" +version = "0.31.1" [profile.release] @@ -55,24 +55,27 @@ opt-level = 2 [workspace.dependencies] -emath = { version = "0.30.0", path = "crates/emath", default-features = false } -ecolor = { version = "0.30.0", path = "crates/ecolor", default-features = false } -epaint = { version = "0.30.0", path = "crates/epaint", default-features = false } -epaint_default_fonts = { version = "0.30.0", path = "crates/epaint_default_fonts" } -egui = { version = "0.30.0", path = "crates/egui", default-features = false } -egui-winit = { version = "0.30.0", path = "crates/egui-winit", default-features = false } -egui_extras = { version = "0.30.0", path = "crates/egui_extras", default-features = false } -egui-wgpu = { version = "0.30.0", path = "crates/egui-wgpu", default-features = false } -egui_demo_lib = { version = "0.30.0", path = "crates/egui_demo_lib", default-features = false } -egui_glow = { version = "0.30.0", path = "crates/egui_glow", default-features = false } -egui_kittest = { version = "0.30.0", path = "crates/egui_kittest", default-features = false } -eframe = { version = "0.30.0", path = "crates/eframe", default-features = false } +emath = { version = "0.31.1", path = "crates/emath", default-features = false } +ecolor = { version = "0.31.1", path = "crates/ecolor", default-features = false } +epaint = { version = "0.31.1", path = "crates/epaint", default-features = false } +epaint_default_fonts = { version = "0.31.1", path = "crates/epaint_default_fonts" } +egui = { version = "0.31.1", path = "crates/egui", default-features = false } +egui-winit = { version = "0.31.1", path = "crates/egui-winit", default-features = false } +egui_extras = { version = "0.31.1", path = "crates/egui_extras", default-features = false } +egui-wgpu = { version = "0.31.1", path = "crates/egui-wgpu", default-features = false } +egui_demo_lib = { version = "0.31.1", path = "crates/egui_demo_lib", default-features = false } +egui_glow = { version = "0.31.1", path = "crates/egui_glow", default-features = false } +egui_kittest = { version = "0.31.1", path = "crates/egui_kittest", default-features = false } +eframe = { version = "0.31.1", path = "crates/eframe", default-features = false } +accesskit = "0.18.0" +accesskit_winit = "0.24" ahash = { version = "0.8.11", default-features = false, features = [ "no-rng", # we don't need DOS-protection, so we let users opt-in to it instead "std", ] } backtrace = "0.3" +bitflags = "2.6" bytemuck = "1.7.2" criterion = { version = "0.5.1", default-features = false } dify = { version = "0.7", default-features = false } @@ -82,7 +85,7 @@ glutin = { version = "0.32.0", default-features = false } glutin-winit = { version = "0.5.0", default-features = false } home = "0.5.9" image = { version = "0.25", default-features = false } -kittest = { version = "0.1" } +kittest = { version = "0.1.0", git = "https://github.com/rerun-io/kittest", branch = "main" } log = { version = "0.4", features = ["std"] } nohash-hasher = "0.2" parking_lot = "0.12" @@ -99,7 +102,7 @@ wasm-bindgen = "0.2" wasm-bindgen-futures = "0.4" web-sys = "0.3.70" web-time = "1.1.0" # Timekeeping for native and web -wgpu = { version = "23.0.0", default-features = false } +wgpu = { version = "24.0.0", default-features = false } windows-sys = "0.59" winit = { version = "0.30.7", default-features = false } @@ -113,6 +116,7 @@ rust_2018_idioms = { level = "warn", priority = -1 } rust_2021_prelude_collisions = "warn" semicolon_in_expressions_from_macros = "warn" trivial_numeric_casts = "warn" +unexpected_cfgs = "warn" unsafe_op_in_unsafe_fn = "warn" # `unsafe_op_in_unsafe_fn` may become the default in future Rust versions: https://github.com/rust-lang/rust/issues/71668 unused_extern_crates = "warn" unused_import_braces = "warn" @@ -202,8 +206,8 @@ match_same_arms = "warn" match_wild_err_arm = "warn" match_wildcard_for_single_variants = "warn" mem_forget = "warn" -mismatched_target_os = "warn" mismatching_type_param_order = "warn" +missing_assert_message = "warn" missing_enforced_import_renames = "warn" missing_errors_doc = "warn" missing_safety_doc = "warn" @@ -271,7 +275,6 @@ zero_sized_map_values = "warn" # TODO(emilk): maybe enable more of these lints? iter_over_hash_type = "allow" -missing_assert_message = "allow" should_panic_without_expect = "allow" too_many_lines = "allow" unwrap_used = "allow" # TODO(emilk): We really wanna warn on this one diff --git a/README.md b/README.md index 247ad32d9..63e5f3ada 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@
- + egui development is sponsored by [Rerun](https://www.rerun.io/), a startup building
an SDK for visualizing streams of multimodal data. @@ -46,7 +46,7 @@ ui.label(format!("Hello '{name}', age {age}")); ui.image(egui::include_image!("ferris.png")); ``` -Dark mode     Light mode +Dark mode     Light mode ## Sections: @@ -133,26 +133,26 @@ Still, egui can be used to create professional looking applications, like [the R * Label text selection * And more! -Check out the [3rd party egui crates wiki](https://github.com/emilk/egui/wiki/3rd-party-egui-crates) for even more +Check out the [3rd party egui crates wiki](https://github.com/emilk/egui/wiki/3rd-party-egui-crates) for even more widgets and features, maintained by the community. - + Light Theme: - - + ## Dependencies `egui` has a minimal set of default dependencies: * [`ab_glyph`](https://crates.io/crates/ab_glyph) * [`ahash`](https://crates.io/crates/ahash) +* [`bitflags`](https://crates.io/crates/bitflags) * [`nohash-hasher`](https://crates.io/crates/nohash-hasher) * [`parking_lot`](https://crates.io/crates/parking_lot) Heavier dependencies are kept out of `egui`, even as opt-in. -No code that isn't fully Wasm-friendly is part of `egui`. +All code in `egui` is Wasm-friendly (even outside a browser). To load images into `egui` you can use the official [`egui_extras`](https://github.com/emilk/egui/tree/master/crates/egui_extras) crate. @@ -190,7 +190,7 @@ These are the official egui integrations: ### 3rd party integrations -Check the wiki to find [3rd party integrations](https://github.com/emilk/egui/wiki/3rd-party-integrations) +Check the wiki to find [3rd party integrations](https://github.com/emilk/egui/wiki/3rd-party-integrations) and [egui crates](https://github.com/emilk/egui/wiki/3rd-party-egui-crates). ### Writing your own egui integration @@ -267,7 +267,7 @@ This is not yet as powerful as say CSS, [but this is going to improve](https://g Here is an example (from https://github.com/a-liashenko/TinyPomodoro): - + ### How do I use egui with `async`? If you call `.await` in your GUI code, the UI will freeze, which is very bad UX. Instead, keep the GUI thread non-blocking and communicate with any concurrent tasks (`async` tasks or other threads) with something like: @@ -375,7 +375,7 @@ Default fonts: ---
- + egui development is sponsored by [Rerun](https://www.rerun.io/), a startup building
an SDK for visualizing streams of multimodal data. diff --git a/RELEASES.md b/RELEASES.md index 79bf23bc6..9d87988b7 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -36,23 +36,19 @@ We don't update the MSRV in a patch release, unless we really, really need to. ## Release testing * [ ] `cargo r -p egui_demo_app` and click around for while -* [ ] `./scripts/build_demo_web.sh --release -g` - - check frame-rate and wasm size - - test on mobile - - test on chromium - - check the in-browser profiler -* [ ] check the color test * [ ] update `eframe_template` and test * [ ] update `egui_plot` and test +* [ ] update `egui_table` and test * [ ] update `egui_tiles` and test * [ ] test with Rerun * [ ] `./scripts/check.sh` * [ ] check that CI is green ## Preparation +* [ ] make sure there are no important unmerged PRs * [ ] run `scripts/generate_example_screenshots.sh` if needed -* [ ] write a short release note that fits in a tweet -* [ ] record gif for `CHANGELOG.md` release note (and later twitter post) +* [ ] write a short release note that fits in a bluesky post +* [ ] record gif for `CHANGELOG.md` release note (and later bluesky post) * [ ] update changelogs using `scripts/generate_changelog.py --version 0.x.0 --write` * [ ] bump version numbers in workspace `Cargo.toml` @@ -60,9 +56,9 @@ We don't update the MSRV in a patch release, unless we really, really need to. I usually do this all on the `master` branch, but doing it in a release branch is also fine, as long as you remember to merge it into `master` later. * [ ] Run `typos` -* [ ] `git commit -m 'Release 0.x.0 - summary'` +* [ ] `git commit -m 'Release 0.x.0 - '` * [ ] `cargo publish` (see below) -* [ ] `git tag -a 0.x.0 -m 'Release 0.x.0 - summary'` +* [ ] `git tag -a 0.x.0 -m 'Release 0.x.0 - '` * [ ] `git pull --tags ; git tag -d latest && git tag -a latest -m 'Latest release' && git push --tags origin latest --force ; git push --tags` * [ ] merge release PR or push to `master` * [ ] check that CI is green @@ -79,15 +75,17 @@ I usually do this all on the `master` branch, but doing it in a release branch i (cd crates/egui && cargo publish --quiet) && echo "βœ… egui" (cd crates/egui-winit && cargo publish --quiet) && echo "βœ… egui-winit" (cd crates/egui-wgpu && cargo publish --quiet) && echo "βœ… egui-wgpu" +(cd crates/eframe && cargo publish --quiet) && echo "βœ… eframe" (cd crates/egui_kittest && cargo publish --quiet) && echo "βœ… egui_kittest" (cd crates/egui_extras && cargo publish --quiet) && echo "βœ… egui_extras" (cd crates/egui_demo_lib && cargo publish --quiet) && echo "βœ… egui_demo_lib" (cd crates/egui_glow && cargo publish --quiet) && echo "βœ… egui_glow" -(cd crates/eframe && cargo publish --quiet) && echo "βœ… eframe" ``` +\ + ## Announcements -* [ ] [twitter](https://x.com/ernerfeldt/status/1772665412225823105) +* [ ] [Bluesky](https://bsky.app/profile/ernerfeldt.bsky.social) * [ ] egui discord * [ ] [r/rust](https://www.reddit.com/r/rust/comments/1bocr5s/announcing_egui_027_with_improved_menus_and/) * [ ] [r/programming](https://www.reddit.com/r/programming/comments/1bocsf6/announcing_egui_027_an_easytouse_crossplatform/) @@ -98,3 +96,5 @@ I usually do this all on the `master` branch, but doing it in a release branch i * [ ] publish new `egui_plot` * [ ] publish new `egui_table` * [ ] publish new `egui_tiles` +* [ ] make a PR to `egui_commonmark` +* [ ] make a PR to `rerun` diff --git a/clippy.toml b/clippy.toml index 9e5fdd1e5..f349943a9 100644 --- a/clippy.toml +++ b/clippy.toml @@ -3,7 +3,7 @@ # ----------------------------------------------------------------------------- # Section identical to scripts/clippy_wasm/clippy.toml: -msrv = "1.80" +msrv = "1.81" allow-unwrap-in-tests = true diff --git a/crates/ecolor/CHANGELOG.md b/crates/ecolor/CHANGELOG.md index 37b5555b6..88d510dba 100644 --- a/crates/ecolor/CHANGELOG.md +++ b/crates/ecolor/CHANGELOG.md @@ -6,6 +6,14 @@ This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.1 - 2025-03-05 +Nothing new + + +## 0.31.0 - 2025-02-04 +* Add `Color32::CYAN` and `Color32::MAGENTA` [#5663](https://github.com/emilk/egui/pull/5663) by [@juancampa](https://github.com/juancampa) + + ## 0.30.0 - 2024-12-16 * Use boxed slice for lookup table to avoid stack overflow [#5212](https://github.com/emilk/egui/pull/5212) by [@YgorSouza](https://github.com/YgorSouza) * Add `Color32::mul` [#5437](https://github.com/emilk/egui/pull/5437) by [@emilk](https://github.com/emilk) diff --git a/crates/ecolor/README.md b/crates/ecolor/README.md index 4c84da019..98e3c3725 100644 --- a/crates/ecolor/README.md +++ b/crates/ecolor/README.md @@ -8,4 +8,6 @@ A simple color storage and conversion library. -Made for [`egui`](https://github.com/emilk/egui/). +This crate is built for the wants and needs of [`egui`](https://github.com/emilk/egui/). + +If you want an actual _good_ color crate, use [`color`](https://crates.io/crates/color) instead. diff --git a/crates/ecolor/src/color32.rs b/crates/ecolor/src/color32.rs index c38a8d6b9..72d0496ee 100644 --- a/crates/ecolor/src/color32.rs +++ b/crates/ecolor/src/color32.rs @@ -5,10 +5,24 @@ use crate::{fast_round, linear_f32_from_linear_u8, Rgba}; /// Instead of manipulating this directly it is often better /// to first convert it to either [`Rgba`] or [`crate::Hsva`]. /// -/// Internally this uses 0-255 gamma space `sRGBA` color with premultiplied alpha. -/// Alpha channel is in linear space. +/// Internally this uses 0-255 gamma space `sRGBA` color with _premultiplied alpha_. /// -/// The special value of alpha=0 means the color is to be treated as an additive color. +/// It's the non-linear ("gamma") values that are multiplied with the alpha. +/// +/// Premultiplied alpha means that the color values have been pre-multiplied with the alpha (opacity). +/// This is in contrast with "normal" RGBA, where the alpha is _separate_ (or "unmultiplied"). +/// Using premultiplied alpha has some advantages: +/// * It allows encoding additive colors +/// * It is the better way to blend colors, e.g. when filtering texture colors +/// * Because the above, it is the better way to encode colors in a GPU texture +/// +/// The color space is assumed to be [sRGB](https://en.wikipedia.org/wiki/SRGB). +/// +/// All operations on `Color32` are done in "gamma space" (see ). +/// This is not physically correct, but it is fast and sometimes more perceptually even than linear space. +/// If you instead want to perform these operations in linear-space color, use [`Rgba`]. +/// +/// An `alpha=0` means the color is to be treated as an additive color. #[repr(C)] #[derive(Clone, Copy, Default, Eq, Hash, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -16,6 +30,7 @@ use crate::{fast_round, linear_f32_from_linear_u8, Rgba}; pub struct Color32(pub(crate) [u8; 4]); impl std::fmt::Debug for Color32 { + /// Prints the contents with premultiplied alpha! fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let [r, g, b, a] = self.0; write!(f, "#{r:02X}_{g:02X}_{b:02X}_{a:02X}") @@ -56,7 +71,10 @@ impl Color32 { pub const RED: Self = Self::from_rgb(255, 0, 0); pub const LIGHT_RED: Self = Self::from_rgb(255, 128, 128); + pub const CYAN: Self = Self::from_rgb(0, 255, 255); + pub const MAGENTA: Self = Self::from_rgb(255, 0, 255); pub const YELLOW: Self = Self::from_rgb(255, 255, 0); + pub const ORANGE: Self = Self::from_rgb(255, 165, 0); pub const LIGHT_YELLOW: Self = Self::from_rgb(255, 255, 0xE0); pub const KHAKI: Self = Self::from_rgb(240, 230, 140); @@ -69,6 +87,8 @@ impl Color32 { pub const BLUE: Self = Self::from_rgb(0, 0, 255); pub const LIGHT_BLUE: Self = Self::from_rgb(0xAD, 0xD8, 0xE6); + pub const PURPLE: Self = Self::from_rgb(0x80, 0, 0x80); + pub const GOLD: Self = Self::from_rgb(255, 215, 0); pub const DEBUG_COLOR: Self = Self::from_rgba_premultiplied(0, 200, 0, 128); @@ -85,41 +105,49 @@ impl Color32 { #[deprecated = "Renamed to PLACEHOLDER"] pub const TEMPORARY_COLOR: Self = Self::PLACEHOLDER; + /// From RGB with alpha of 255 (opaque). #[inline] pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self { Self([r, g, b, 255]) } + /// From RGB into an additive color (will make everything it blend with brighter). #[inline] pub const fn from_rgb_additive(r: u8, g: u8, b: u8) -> Self { Self([r, g, b, 0]) } /// From `sRGBA` with premultiplied alpha. + /// + /// You likely want to use [`Self::from_rgba_unmultiplied`] instead. #[inline] pub const fn from_rgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { Self([r, g, b, a]) } - /// From `sRGBA` WITHOUT premultiplied alpha. + /// From `sRGBA` with separate alpha. + /// + /// This is a "normal" RGBA value that you would find in a color picker or a table somewhere. + /// + /// You can use [`Self::to_srgba_unmultiplied`] to get back these values, + /// but for transparent colors what you get back might be slightly different (rounding errors). #[inline] pub fn from_rgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { use std::sync::OnceLock; match a { - // common-case optimization + // common-case optimization: 0 => Self::TRANSPARENT, - // common-case optimization + + // common-case optimization: 255 => Self::from_rgb(r, g, b), + a => { static LOOKUP_TABLE: OnceLock> = OnceLock::new(); let lut = LOOKUP_TABLE.get_or_init(|| { - use crate::{gamma_u8_from_linear_f32, linear_f32_from_gamma_u8}; (0..=u16::MAX) .map(|i| { let [value, alpha] = i.to_ne_bytes(); - let value_lin = linear_f32_from_gamma_u8(value); - let alpha_lin = linear_f32_from_linear_u8(alpha); - gamma_u8_from_linear_f32(value_lin * alpha_lin) + fast_round(value as f32 * linear_f32_from_linear_u8(alpha)) }) .collect() }); @@ -131,22 +159,26 @@ impl Color32 { } } + /// Opaque gray. #[doc(alias = "from_grey")] #[inline] pub const fn from_gray(l: u8) -> Self { Self([l, l, l, 255]) } + /// Black with the given opacity. #[inline] pub const fn from_black_alpha(a: u8) -> Self { Self([0, 0, 0, a]) } + /// White with the given opacity. #[inline] pub fn from_white_alpha(a: u8) -> Self { - Rgba::from_white_alpha(linear_f32_from_linear_u8(a)).into() + Self([a, a, a, a]) } + /// Additive white. #[inline] pub const fn from_additive_luminance(l: u8) -> Self { Self([l, l, l, 0]) @@ -157,21 +189,25 @@ impl Color32 { self.a() == 255 } + /// Red component multiplied by alpha. #[inline] pub const fn r(&self) -> u8 { self.0[0] } + /// Green component multiplied by alpha. #[inline] pub const fn g(&self) -> u8 { self.0[1] } + /// Blue component multiplied by alpha. #[inline] pub const fn b(&self) -> u8 { self.0[2] } + /// Alpha (opacity). #[inline] pub const fn a(&self) -> u8 { self.0[3] @@ -208,9 +244,26 @@ impl Color32 { (self.r(), self.g(), self.b(), self.a()) } + /// Convert to a normal "unmultiplied" RGBA color (i.e. with separate alpha). + /// + /// This will unmultiply the alpha. + /// + /// This is the inverse of [`Self::from_rgba_unmultiplied`], + /// but due to precision problems it may return slightly different values for transparent colors. #[inline] pub fn to_srgba_unmultiplied(&self) -> [u8; 4] { - Rgba::from(*self).to_srgba_unmultiplied() + let [r, g, b, a] = self.to_array(); + match a { + // Common-case optimization. + 0 | 255 => self.to_array(), + a => { + let factor = 255.0 / a as f32; + let r = fast_round(factor * r as f32); + let g = fast_round(factor * g as f32); + let b = fast_round(factor * b as f32); + [r, g, b, a] + } + } } /// Multiply with 0.5 to make color half as opaque, perceptually. @@ -220,7 +273,10 @@ impl Color32 { /// This is perceptually even, and faster that [`Self::linear_multiply`]. #[inline] pub fn gamma_multiply(self, factor: f32) -> Self { - debug_assert!(0.0 <= factor && factor.is_finite()); + debug_assert!( + 0.0 <= factor && factor.is_finite(), + "factor should be finite, but was {factor}" + ); let Self([r, g, b, a]) = self; Self([ (r as f32 * factor + 0.5) as u8, @@ -230,13 +286,33 @@ impl Color32 { ]) } + /// Multiply with 127 to make color half as opaque, perceptually. + /// + /// Fast multiplication in gamma-space. + /// + /// This is perceptually even, and faster that [`Self::linear_multiply`]. + #[inline] + pub fn gamma_multiply_u8(self, factor: u8) -> Self { + let Self([r, g, b, a]) = self; + let factor = factor as u32; + Self([ + ((r as u32 * factor + 127) / 255) as u8, + ((g as u32 * factor + 127) / 255) as u8, + ((b as u32 * factor + 127) / 255) as u8, + ((a as u32 * factor + 127) / 255) as u8, + ]) + } + /// Multiply with 0.5 to make color half as opaque in linear space. /// /// This is using linear space, which is not perceptually even. /// You likely want to use [`Self::gamma_multiply`] instead. #[inline] pub fn linear_multiply(self, factor: f32) -> Self { - debug_assert!(0.0 <= factor && factor.is_finite()); + debug_assert!( + 0.0 <= factor && factor.is_finite(), + "factor should be finite, but was {factor}" + ); // As an unfortunate side-effect of using premultiplied alpha // we need a somewhat expensive conversion to linear space and back. Rgba::from(self).multiply(factor).into() @@ -268,6 +344,19 @@ impl Color32 { fast_round(lerp((self[3] as f32)..=(other[3] as f32), t)), ) } + + /// Blend two colors in gamma space, so that `self` is behind the argument. + pub fn blend(self, on_top: Self) -> Self { + self.gamma_multiply_u8(255 - on_top.a()) + on_top + } + + /// Intensity of the color. + /// + /// Returns a value in the range 0-1. + /// The brighter the color, the closer to 1. + pub fn intensity(&self) -> f32 { + (self.r() as f32 * 0.299 + self.g() as f32 * 0.587 + self.b() as f32 * 0.114) / 255.0 + } } impl std::ops::Mul for Color32 { @@ -284,3 +373,145 @@ impl std::ops::Mul for Color32 { ]) } } + +impl std::ops::Add for Color32 { + type Output = Self; + + #[inline] + fn add(self, other: Self) -> Self { + Self([ + self[0].saturating_add(other[0]), + self[1].saturating_add(other[1]), + self[2].saturating_add(other[2]), + self[3].saturating_add(other[3]), + ]) + } +} + +#[cfg(test)] +mod test { + use super::*; + + fn test_rgba() -> impl Iterator { + [ + [0, 0, 0, 0], + [0, 0, 0, 255], + [10, 0, 30, 0], + [10, 0, 30, 40], + [10, 100, 200, 0], + [10, 100, 200, 100], + [10, 100, 200, 200], + [10, 100, 200, 255], + [10, 100, 200, 40], + [10, 20, 0, 0], + [10, 20, 0, 255], + [10, 20, 30, 255], + [10, 20, 30, 40], + [255, 255, 255, 0], + [255, 255, 255, 255], + ] + .into_iter() + } + + #[test] + fn test_color32_additive() { + let opaque = Color32::from_rgb(40, 50, 60); + let additive = Color32::from_rgb(255, 127, 10).additive(); + assert_eq!(additive.blend(opaque), opaque, "opaque on top of additive"); + assert_eq!( + opaque.blend(additive), + Color32::from_rgb(255, 177, 70), + "additive on top of opaque" + ); + } + + #[test] + fn test_color32_blend_vs_gamma_blend() { + let opaque = Color32::from_rgb(0x60, 0x60, 0x60); + let transparent = Color32::from_rgba_unmultiplied(168, 65, 65, 79); + assert_eq!( + transparent.blend(opaque), + opaque, + "Opaque on top of transparent" + ); + // Blending in gamma-space is the de-facto standard almost everywhere. + // Browsers and most image editors do it, and so it is what users expect. + assert_eq!( + opaque.blend(transparent), + Color32::from_rgb( + blend(0x60, 168, 79), + blend(0x60, 65, 79), + blend(0x60, 65, 79) + ), + "Transparent on top of opaque" + ); + + fn blend(dest: u8, src: u8, alpha: u8) -> u8 { + let src = src as f32 / 255.0; + let dest = dest as f32 / 255.0; + let alpha = alpha as f32 / 255.0; + fast_round((src * alpha + dest * (1.0 - alpha)) * 255.0) + } + } + + #[test] + fn color32_unmultiplied_round_trip() { + for in_rgba in test_rgba() { + let [r, g, b, a] = in_rgba; + if a == 0 { + continue; + } + + let c = Color32::from_rgba_unmultiplied(r, g, b, a); + let out_rgba = c.to_srgba_unmultiplied(); + + if a == 255 { + assert_eq!(in_rgba, out_rgba); + } else { + // There will be small rounding errors whenever the alpha is not 0 or 255, + // because we multiply and then unmultiply the alpha. + for (&a, &b) in in_rgba.iter().zip(out_rgba.iter()) { + assert!(a.abs_diff(b) <= 3, "{in_rgba:?} != {out_rgba:?}"); + } + } + } + } + + #[test] + fn from_black_white_alpha() { + for a in 0..=255 { + assert_eq!( + Color32::from_white_alpha(a), + Color32::from_rgba_unmultiplied(255, 255, 255, a) + ); + assert_eq!( + Color32::from_white_alpha(a), + Color32::WHITE.gamma_multiply_u8(a) + ); + + assert_eq!( + Color32::from_black_alpha(a), + Color32::from_rgba_unmultiplied(0, 0, 0, a) + ); + assert_eq!( + Color32::from_black_alpha(a), + Color32::BLACK.gamma_multiply_u8(a) + ); + } + } + + #[test] + fn to_from_rgba() { + for [r, g, b, a] in test_rgba() { + let original = Color32::from_rgba_unmultiplied(r, g, b, a); + let rgba = Rgba::from(original); + let back = Color32::from(rgba); + assert_eq!(back, original); + } + + assert_eq!( + Color32::from(Rgba::from_rgba_unmultiplied(1.0, 0.0, 0.0, 0.5)), + Color32::from_rgba_unmultiplied(255, 0, 0, 128) + ); + } +} diff --git a/crates/ecolor/src/hex_color_runtime.rs b/crates/ecolor/src/hex_color_runtime.rs index 5b20258fa..21e07ffc4 100644 --- a/crates/ecolor/src/hex_color_runtime.rs +++ b/crates/ecolor/src/hex_color_runtime.rs @@ -90,14 +90,15 @@ impl HexColor { let [r, gb] = u16::from_str_radix(s, 16) .map_err(ParseHexColorError::InvalidInt)? .to_be_bytes(); - let [r, g, b] = [r, gb >> 4, gb & 0x0f].map(|u| u << 4 | u); + let [r, g, b] = [r, gb >> 4, gb & 0x0f].map(|u| (u << 4) | u); Ok(Self::Hex3(Color32::from_rgb(r, g, b))) } 4 => { let [r_g, b_a] = u16::from_str_radix(s, 16) .map_err(ParseHexColorError::InvalidInt)? .to_be_bytes(); - let [r, g, b, a] = [r_g >> 4, r_g & 0x0f, b_a >> 4, b_a & 0x0f].map(|u| u << 4 | u); + let [r, g, b, a] = + [r_g >> 4, r_g & 0x0f, b_a >> 4, b_a & 0x0f].map(|u| (u << 4) | u); Ok(Self::Hex4(Color32::from_rgba_unmultiplied(r, g, b, a))) } 6 => { @@ -207,17 +208,22 @@ mod tests { #[test] fn hex_string_round_trip() { - use Color32 as C; let cases = [ - C::from_rgba_unmultiplied(10, 20, 30, 0), - C::from_rgba_unmultiplied(10, 20, 30, 40), - C::from_rgba_unmultiplied(10, 20, 30, 255), - C::from_rgba_unmultiplied(0, 20, 30, 0), - C::from_rgba_unmultiplied(10, 0, 30, 40), - C::from_rgba_unmultiplied(10, 20, 0, 255), + [0, 20, 30, 0], + [10, 0, 30, 40], + [10, 100, 200, 0], + [10, 100, 200, 100], + [10, 100, 200, 200], + [10, 100, 200, 255], + [10, 100, 200, 40], + [10, 20, 0, 255], + [10, 20, 30, 0], + [10, 20, 30, 255], + [10, 20, 30, 40], ]; - for color in cases { - assert_eq!(C::from_hex(color.to_hex().as_str()), Ok(color)); + for [r, g, b, a] in cases { + let color = Color32::from_rgba_unmultiplied(r, g, b, a); + assert_eq!(Color32::from_hex(color.to_hex().as_str()), Ok(color)); } } } diff --git a/crates/ecolor/src/hsva.rs b/crates/ecolor/src/hsva.rs index 5f5430cf0..02ffd67d6 100644 --- a/crates/ecolor/src/hsva.rs +++ b/crates/ecolor/src/hsva.rs @@ -1,6 +1,5 @@ use crate::{ - gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_linear_u8, - linear_u8_from_linear_f32, Color32, Rgba, + gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_u8_from_linear_f32, Color32, Rgba, }; /// Hue, saturation, value, alpha. All in the range [0, 1]. @@ -29,30 +28,20 @@ impl Hsva { /// From `sRGBA` with premultiplied alpha #[inline] pub fn from_srgba_premultiplied([r, g, b, a]: [u8; 4]) -> Self { - Self::from_rgba_premultiplied( - linear_f32_from_gamma_u8(r), - linear_f32_from_gamma_u8(g), - linear_f32_from_gamma_u8(b), - linear_f32_from_linear_u8(a), - ) + Self::from(Color32::from_rgba_premultiplied(r, g, b, a)) } /// From `sRGBA` without premultiplied alpha #[inline] pub fn from_srgba_unmultiplied([r, g, b, a]: [u8; 4]) -> Self { - Self::from_rgba_unmultiplied( - linear_f32_from_gamma_u8(r), - linear_f32_from_gamma_u8(g), - linear_f32_from_gamma_u8(b), - linear_f32_from_linear_u8(a), - ) + Self::from(Color32::from_rgba_unmultiplied(r, g, b, a)) } /// From linear RGBA with premultiplied alpha #[inline] pub fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { #![allow(clippy::many_single_char_names)] - if a == 0.0 { + if a <= 0.0 { if r == 0.0 && b == 0.0 && a == 0.0 { Self::default() } else { @@ -152,13 +141,7 @@ impl Hsva { #[inline] pub fn to_srgba_premultiplied(&self) -> [u8; 4] { - let [r, g, b, a] = self.to_rgba_premultiplied(); - [ - gamma_u8_from_linear_f32(r), - gamma_u8_from_linear_f32(g), - gamma_u8_from_linear_f32(b), - linear_u8_from_linear_f32(a), - ] + Color32::from(*self).to_array() } /// To gamma-space 0-255. diff --git a/crates/ecolor/src/lib.rs b/crates/ecolor/src/lib.rs index a9400d9de..9c4ac9b66 100644 --- a/crates/ecolor/src/lib.rs +++ b/crates/ecolor/src/lib.rs @@ -1,9 +1,20 @@ //! Color conversions and types. //! +//! This crate is built for the wants and needs of [`egui`](https://github.com/emilk/egui/). +//! +//! If you want an actual _good_ color crate, use [`color`](https://crates.io/crates/color) instead. +//! //! If you want a compact color representation, use [`Color32`]. -//! If you want to manipulate RGBA colors use [`Rgba`]. +//! If you want to manipulate RGBA colors in linear space use [`Rgba`]. //! If you want to manipulate colors in a way closer to how humans think about colors, use [`HsvaGamma`]. //! +//! ## Conventions +//! The word "gamma" or "srgb" is used to refer to values in the non-linear space defined by +//! [the sRGB transfer function](https://en.wikipedia.org/wiki/SRGB). +//! We use `u8` for anything in the "gamma" space. +//! +//! We use `f32` in 0-1 range for anything in the linear space. +//! //! ## Feature flags #![cfg_attr(feature = "document-features", doc = document_features::document_features!())] //! @@ -39,23 +50,46 @@ pub use hex_color_runtime::*; impl From for Rgba { fn from(srgba: Color32) -> Self { - Self([ - linear_f32_from_gamma_u8(srgba.0[0]), - linear_f32_from_gamma_u8(srgba.0[1]), - linear_f32_from_gamma_u8(srgba.0[2]), - linear_f32_from_linear_u8(srgba.0[3]), - ]) + let [r, g, b, a] = srgba.to_array(); + if a == 0 { + // Additive, or completely transparent + Self([ + linear_f32_from_gamma_u8(r), + linear_f32_from_gamma_u8(g), + linear_f32_from_gamma_u8(b), + 0.0, + ]) + } else { + let a = linear_f32_from_linear_u8(a); + Self([ + linear_from_gamma(r as f32 / (255.0 * a)) * a, + linear_from_gamma(g as f32 / (255.0 * a)) * a, + linear_from_gamma(b as f32 / (255.0 * a)) * a, + a, + ]) + } } } impl From for Color32 { fn from(rgba: Rgba) -> Self { - Self([ - gamma_u8_from_linear_f32(rgba.0[0]), - gamma_u8_from_linear_f32(rgba.0[1]), - gamma_u8_from_linear_f32(rgba.0[2]), - linear_u8_from_linear_f32(rgba.0[3]), - ]) + let [r, g, b, a] = rgba.to_array(); + if a == 0.0 { + // Additive, or completely transparent + Self([ + gamma_u8_from_linear_f32(r), + gamma_u8_from_linear_f32(g), + gamma_u8_from_linear_f32(b), + 0, + ]) + } else { + Self([ + fast_round(gamma_u8_from_linear_f32(r / a) as f32 * a), + fast_round(gamma_u8_from_linear_f32(g / a) as f32 * a), + fast_round(gamma_u8_from_linear_f32(b / a) as f32 * a), + linear_u8_from_linear_f32(a), + ]) + } } } diff --git a/crates/ecolor/src/rgba.rs b/crates/ecolor/src/rgba.rs index 93cb41d81..99fab41cc 100644 --- a/crates/ecolor/src/rgba.rs +++ b/crates/ecolor/src/rgba.rs @@ -1,9 +1,8 @@ -use crate::{ - gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_linear_u8, - linear_u8_from_linear_f32, -}; +use crate::Color32; /// 0-1 linear space `RGBA` color with premultiplied alpha. +/// +/// See [`crate::Color32`] for explanation of what "premultiplied alpha" means. #[repr(C)] #[derive(Clone, Copy, Debug, Default, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -70,20 +69,12 @@ impl Rgba { #[inline] pub fn from_srgba_premultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { - let r = linear_f32_from_gamma_u8(r); - let g = linear_f32_from_gamma_u8(g); - let b = linear_f32_from_gamma_u8(b); - let a = linear_f32_from_linear_u8(a); - Self::from_rgba_premultiplied(r, g, b, a) + Self::from(Color32::from_rgba_premultiplied(r, g, b, a)) } #[inline] pub fn from_srgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { - let r = linear_f32_from_gamma_u8(r); - let g = linear_f32_from_gamma_u8(g); - let b = linear_f32_from_gamma_u8(b); - let a = linear_f32_from_linear_u8(a); - Self::from_rgba_premultiplied(r * a, g * a, b * a, a) + Self::from(Color32::from_rgba_unmultiplied(r, g, b, a)) } #[inline] @@ -99,15 +90,24 @@ impl Rgba { #[inline] pub fn from_luminance_alpha(l: f32, a: f32) -> Self { - debug_assert!(0.0 <= l && l <= 1.0); - debug_assert!(0.0 <= a && a <= 1.0); + debug_assert!( + 0.0 <= l && l <= 1.0, + "l should be in the range [0, 1], but was {l}" + ); + debug_assert!( + 0.0 <= a && a <= 1.0, + "a should be in the range [0, 1], but was {a}" + ); Self([l * a, l * a, l * a, a]) } /// Transparent black #[inline] pub fn from_black_alpha(a: f32) -> Self { - debug_assert!(0.0 <= a && a <= 1.0); + debug_assert!( + 0.0 <= a && a <= 1.0, + "a should be in the range [0, 1], but was {a}" + ); Self([0.0, 0.0, 0.0, a]) } @@ -211,13 +211,12 @@ impl Rgba { /// unmultiply the alpha #[inline] pub fn to_srgba_unmultiplied(&self) -> [u8; 4] { - let [r, g, b, a] = self.to_rgba_unmultiplied(); - [ - gamma_u8_from_linear_f32(r), - gamma_u8_from_linear_f32(g), - gamma_u8_from_linear_f32(b), - linear_u8_from_linear_f32(a.abs()), - ] + crate::Color32::from(*self).to_srgba_unmultiplied() + } + + /// Blend two colors in linear space, so that `self` is behind the argument. + pub fn blend(self, on_top: Self) -> Self { + self.multiply(1.0 - on_top.a()) + on_top } } @@ -276,3 +275,72 @@ impl std::ops::Mul for f32 { ]) } } + +#[cfg(test)] +mod test { + + use super::*; + + fn test_rgba() -> impl Iterator { + [ + [0, 0, 0, 0], + [0, 0, 0, 255], + [10, 0, 30, 0], + [10, 0, 30, 40], + [10, 100, 200, 0], + [10, 100, 200, 100], + [10, 100, 200, 200], + [10, 100, 200, 255], + [10, 100, 200, 40], + [10, 20, 0, 0], + [10, 20, 0, 255], + [10, 20, 30, 255], + [10, 20, 30, 40], + [255, 255, 255, 0], + [255, 255, 255, 255], + ] + .into_iter() + } + + #[test] + fn test_rgba_blend() { + let opaque = Rgba::from_rgb(0.4, 0.5, 0.6); + let transparent = Rgba::from_rgb(1.0, 0.5, 0.0).multiply(0.3); + assert_eq!( + transparent.blend(opaque), + opaque, + "Opaque on top of transparent" + ); + assert_eq!( + opaque.blend(transparent), + Rgba::from_rgb( + 0.7 * 0.4 + 0.3 * 1.0, + 0.7 * 0.5 + 0.3 * 0.5, + 0.7 * 0.6 + 0.3 * 0.0 + ), + "Transparent on top of opaque" + ); + } + + #[test] + fn test_rgba_roundtrip() { + for in_rgba in test_rgba() { + let [r, g, b, a] = in_rgba; + if a == 0 { + continue; + } + let rgba = Rgba::from_srgba_unmultiplied(r, g, b, a); + let out_rgba = rgba.to_srgba_unmultiplied(); + + if a == 255 { + assert_eq!(in_rgba, out_rgba); + } else { + // There will be small rounding errors whenever the alpha is not 0 or 255, + // because we multiply and then unmultiply the alpha. + for (&a, &b) in in_rgba.iter().zip(out_rgba.iter()) { + assert!(a.abs_diff(b) <= 3, "{in_rgba:?} != {out_rgba:?}"); + } + } + } + } +} diff --git a/crates/eframe/CHANGELOG.md b/crates/eframe/CHANGELOG.md index cbb6cf411..df1697281 100644 --- a/crates/eframe/CHANGELOG.md +++ b/crates/eframe/CHANGELOG.md @@ -1,12 +1,24 @@ # Changelog for eframe All notable changes to the `eframe` crate. -NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/CHANGELOG.md), [`egui_glow`](../egui_glow/CHANGELOG.md),and [`egui-wgpu`](../egui-wgpu/CHANGELOG.md) have their own changelogs! +NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glow`](../egui_glow/CHANGELOG.md),and [`egui-wgpu`](../egui-wgpu/CHANGELOG.md) have their own changelogs! This file is updated upon each release. Changes since the last release can be found at or by running the `scripts/generate_changelog.py` script. +## 0.31.1 - 2025-03-05 +Nothing new + + +## 0.31.0 - 2025-02-04 +* Web: Fix incorrect scale when moving to screen with new DPI [#5631](https://github.com/emilk/egui/pull/5631) by [@emilk](https://github.com/emilk) +* Re-enable IME support on Linux [#5198](https://github.com/emilk/egui/pull/5198) by [@YgorSouza](https://github.com/YgorSouza) +* Serialize window maximized state in `WindowSettings` [#5554](https://github.com/emilk/egui/pull/5554) by [@landaire](https://github.com/landaire) +* Save state on suspend on Android and iOS [#5601](https://github.com/emilk/egui/pull/5601) by [@Pandicon](https://github.com/Pandicon) +* Eframe web: forward cmd-S/O to egui app (stop default browser action) [#5655](https://github.com/emilk/egui/pull/5655) by [@emilk](https://github.com/emilk) + + ## 0.30.0 - 2024-12-16 - Android support NOTE: you now need to enable the `wayland` or `x11` features to get Linux support, including getting it to work on most CI systems. diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index f2384b878..774bcbbe9 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -177,12 +177,14 @@ wgpu = { workspace = true, optional = true, features = [ # mac: [target.'cfg(any(target_os = "macos"))'.dependencies] objc2 = "0.5.1" -objc2-foundation = { version = "0.2.0", features = [ +objc2-foundation = { version = "0.2.0", default-features = false, features = [ + "std", "block2", "NSData", "NSString", ] } -objc2-app-kit = { version = "0.2.0", features = [ +objc2-app-kit = { version = "0.2.0", default-features = false, features = [ + "std", "NSApplication", "NSImage", "NSMenu", @@ -209,6 +211,7 @@ percent-encoding = "2.1" wasm-bindgen.workspace = true wasm-bindgen-futures.workspace = true web-sys = { workspace = true, features = [ + "AddEventListenerOptions", "BinaryType", "Blob", "BlobPropertyBag", @@ -250,6 +253,7 @@ web-sys = { workspace = true, features = [ "ResizeObserverEntry", "ResizeObserverOptions", "ResizeObserverSize", + "ShadowRoot", "Storage", "Touch", "TouchEvent", diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index 7a7e92244..fb589fe2e 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -878,20 +878,6 @@ pub trait Storage { fn flush(&mut self); } -/// Stores nothing. -#[derive(Clone, Default)] -pub(crate) struct DummyStorage {} - -impl Storage for DummyStorage { - fn get_string(&self, _key: &str) -> Option { - None - } - - fn set_string(&mut self, _key: &str, _value: String) {} - - fn flush(&mut self) {} -} - /// Get and deserialize the [RON](https://github.com/ron-rs/ron) stored at the given key. #[cfg(feature = "ron")] pub fn get_value(storage: &dyn Storage, key: &str) -> Option { diff --git a/crates/eframe/src/native/file_storage.rs b/crates/eframe/src/native/file_storage.rs index 346c46b42..7fde2d288 100644 --- a/crates/eframe/src/native/file_storage.rs +++ b/crates/eframe/src/native/file_storage.rs @@ -58,27 +58,34 @@ fn roaming_appdata() -> Option { extern "C" { fn wcslen(buf: *const u16) -> usize; } - unsafe { - let mut path = ptr::null_mut(); - match SHGetKnownFolderPath( + let mut path_raw = ptr::null_mut(); + + // SAFETY: SHGetKnownFolderPath allocates for us, we don't pass any pointers to it. + // See https://learn.microsoft.com/en-us/windows/win32/api/shlobj_core/nf-shlobj_core-shgetknownfolderpath + let result = unsafe { + SHGetKnownFolderPath( &FOLDERID_RoamingAppData, KF_FLAG_DONT_VERIFY as u32, std::ptr::null_mut(), - &mut path, - ) { - S_OK => { - let path_slice = slice::from_raw_parts(path, wcslen(path)); - let s = OsString::from_wide(&path_slice); - CoTaskMemFree(path.cast()); - Some(PathBuf::from(s)) - } - _ => { - // Free any allocated memory even on failure. A null ptr is a no-op for `CoTaskMemFree`. - CoTaskMemFree(path.cast()); - None - } - } - } + &mut path_raw, + ) + }; + + let path = if result == S_OK { + // SAFETY: SHGetKnownFolderPath indicated success and is supposed to allocate a nullterminated string for us. + let path_slice = unsafe { slice::from_raw_parts(path_raw, wcslen(path_raw)) }; + Some(PathBuf::from(OsString::from_wide(path_slice))) + } else { + None + }; + + // SAFETY: + // This memory got allocated by SHGetKnownFolderPath, we didn't touch anything in the process. + // A null ptr is a no-op for `CoTaskMemFree`, so in case this failed we're still good. + // https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cotaskmemfree + unsafe { CoTaskMemFree(path_raw.cast()) }; + + path } #[cfg(any(not(windows), target_vendor = "uwp"))] @@ -89,7 +96,7 @@ fn roaming_appdata() -> Option { // ---------------------------------------------------------------------------- /// A key-value store backed by a [RON](https://github.com/ron-rs/ron) file on disk. -/// Used to restore egui state, glium window position/size and app state. +/// Used to restore egui state, glow window position/size and app state. pub struct FileStorage { ron_filepath: PathBuf, kv: HashMap, diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index f17c6ad50..94b254682 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -272,7 +272,7 @@ impl<'app> GlowWinitApp<'app> { .. } = viewport { - egui_winit.init_accesskit(window, event_loop_proxy); + egui_winit.init_accesskit(event_loop, window, event_loop_proxy); } } @@ -344,7 +344,7 @@ impl<'app> GlowWinitApp<'app> { } } -impl<'app> WinitApp for GlowWinitApp<'app> { +impl WinitApp for GlowWinitApp<'_> { fn egui_ctx(&self) -> Option<&egui::Context> { self.running.as_ref().map(|r| &r.integration.egui_ctx) } @@ -366,6 +366,20 @@ impl<'app> WinitApp for GlowWinitApp<'app> { .and_then(|r| r.glutin.borrow().window_from_viewport.get(&id).copied()) } + fn save(&mut self) { + log::debug!("WinitApp::save called"); + if let Some(running) = self.running.as_mut() { + profiling::function_scope!(); + + // This is used because of the "save on suspend" logic on Android. Once the application is suspended, there is no window associated to it, which was causing panics when `.window().expect()` was used. + let window_opt = running.glutin.borrow().window_opt(ViewportId::ROOT); + + running + .integration + .save(running.app.as_mut(), window_opt.as_deref()); + } + } + fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { profiling::function_scope!(); @@ -413,7 +427,7 @@ impl<'app> WinitApp for GlowWinitApp<'app> { if let Some(running) = &mut self.running { running.glutin.borrow_mut().on_suspend()?; } - Ok(EventResult::Wait) + Ok(EventResult::Save) } fn device_event( @@ -479,7 +493,7 @@ impl<'app> WinitApp for GlowWinitApp<'app> { } } -impl<'app> GlowWinitRunning<'app> { +impl GlowWinitRunning<'_> { fn run_ui_and_paint( &mut self, event_loop: &ActiveEventLoop, @@ -1214,10 +1228,12 @@ impl GlutinWindowContext { .expect("viewport doesn't exist") } + fn window_opt(&self, viewport_id: ViewportId) -> Option> { + self.viewport(viewport_id).window.clone() + } + fn window(&self, viewport_id: ViewportId) -> Arc { - self.viewport(viewport_id) - .window - .clone() + self.window_opt(viewport_id) .expect("winit window doesn't exist") } diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index e328877a4..641459b52 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -89,47 +89,56 @@ impl WinitAppWrapper { event_result: Result, ) { let mut exit = false; + let mut save = false; log::trace!("event_result: {event_result:?}"); - let combined_result = event_result.and_then(|event_result| { - match event_result { - EventResult::Wait => { - event_loop.set_control_flow(ControlFlow::Wait); - Ok(event_result) - } - EventResult::RepaintNow(window_id) => { - log::trace!("RepaintNow of {window_id:?}",); + let mut event_result = event_result; - if cfg!(target_os = "windows") { - // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 - self.winit_app.run_ui_and_paint(event_loop, window_id) - } else { - // Fix for https://github.com/emilk/egui/issues/2425 - self.windows_next_repaint_times - .insert(window_id, Instant::now()); - Ok(event_result) - } - } - EventResult::RepaintNext(window_id) => { - log::trace!("RepaintNext of {window_id:?}",); + if cfg!(target_os = "windows") { + if let Ok(EventResult::RepaintNow(window_id)) = event_result { + log::trace!("RepaintNow of {window_id:?}"); + self.windows_next_repaint_times + .insert(window_id, Instant::now()); + + // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 + event_result = self.winit_app.run_ui_and_paint(event_loop, window_id); + } + } + + let combined_result = event_result.map(|event_result| match event_result { + EventResult::Wait => { + event_loop.set_control_flow(ControlFlow::Wait); + event_result + } + EventResult::RepaintNow(window_id) => { + log::trace!("RepaintNow of {window_id:?}",); + self.windows_next_repaint_times + .insert(window_id, Instant::now()); + event_result + } + EventResult::RepaintNext(window_id) => { + log::trace!("RepaintNext of {window_id:?}",); + self.windows_next_repaint_times + .insert(window_id, Instant::now()); + event_result + } + EventResult::RepaintAt(window_id, repaint_time) => { + self.windows_next_repaint_times.insert( + window_id, self.windows_next_repaint_times - .insert(window_id, Instant::now()); - Ok(event_result) - } - EventResult::RepaintAt(window_id, repaint_time) => { - self.windows_next_repaint_times.insert( - window_id, - self.windows_next_repaint_times - .get(&window_id) - .map_or(repaint_time, |last| (*last).min(repaint_time)), - ); - Ok(event_result) - } - EventResult::Exit => { - exit = true; - Ok(event_result) - } + .get(&window_id) + .map_or(repaint_time, |last| (*last).min(repaint_time)), + ); + event_result + } + EventResult::Save => { + save = true; + event_result + } + EventResult::Exit => { + exit = true; + event_result } }); @@ -139,6 +148,11 @@ impl WinitAppWrapper { self.return_result = Err(err); }; + if save { + log::debug!("Received an EventResult::Save - saving app state"); + self.winit_app.save(); + } + if exit { if self.run_and_return { log::debug!("Asking to exit event loop…"); diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 93643c223..60484cdee 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -183,7 +183,7 @@ impl<'app> WgpuWinitApp<'app> { ) -> crate::Result<&mut WgpuWinitRunning<'app>> { profiling::function_scope!(); #[allow(unsafe_code, unused_mut, unused_unsafe)] - let mut painter = egui_wgpu::winit::Painter::new( + let mut painter = pollster::block_on(egui_wgpu::winit::Painter::new( egui_ctx.clone(), self.native_options.wgpu_options.clone(), self.native_options.multisampling.max(1) as _, @@ -193,7 +193,7 @@ impl<'app> WgpuWinitApp<'app> { ), self.native_options.viewport.transparent.unwrap_or(false), self.native_options.dithering, - ); + )); let window = Arc::new(window); @@ -249,7 +249,7 @@ impl<'app> WgpuWinitApp<'app> { #[cfg(feature = "accesskit")] { let event_loop_proxy = self.repaint_proxy.lock().clone(); - egui_winit.init_accesskit(&window, event_loop_proxy); + egui_winit.init_accesskit(event_loop, &window, event_loop_proxy); } let app_creator = std::mem::take(&mut self.app_creator) @@ -323,7 +323,7 @@ impl<'app> WgpuWinitApp<'app> { } } -impl<'app> WinitApp for WgpuWinitApp<'app> { +impl WinitApp for WgpuWinitApp<'_> { fn egui_ctx(&self) -> Option<&egui::Context> { self.running.as_ref().map(|r| &r.integration.egui_ctx) } @@ -355,6 +355,13 @@ impl<'app> WinitApp for WgpuWinitApp<'app> { ) } + fn save(&mut self) { + log::debug!("WinitApp::save called"); + if let Some(running) = self.running.as_mut() { + running.save(); + } + } + fn save_and_destroy(&mut self) { if let Some(mut running) = self.running.take() { running.save_and_destroy(); @@ -415,7 +422,7 @@ impl<'app> WinitApp for WgpuWinitApp<'app> { fn suspended(&mut self, _: &ActiveEventLoop) -> crate::Result { #[cfg(target_os = "android")] self.drop_window()?; - Ok(EventResult::Wait) + Ok(EventResult::Save) } fn device_event( @@ -487,14 +494,24 @@ impl<'app> WinitApp for WgpuWinitApp<'app> { } } -impl<'app> WgpuWinitRunning<'app> { +impl WgpuWinitRunning<'_> { + /// Saves the application state + fn save(&mut self) { + let shared = self.shared.borrow(); + // This is done because of the "save on suspend" logic on Android. Once the application is suspended, there is no window associated to it. + let window = if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) + { + window.as_deref() + } else { + None + }; + self.integration.save(self.app.as_mut(), window); + } + fn save_and_destroy(&mut self) { profiling::function_scope!(); - let mut shared = self.shared.borrow_mut(); - if let Some(Viewport { window, .. }) = shared.viewports.get(&ViewportId::ROOT) { - self.integration.save(self.app.as_mut(), window.as_deref()); - } + self.save(); #[cfg(feature = "glow")] self.app.on_exit(None); @@ -502,6 +519,7 @@ impl<'app> WgpuWinitRunning<'app> { #[cfg(not(feature = "glow"))] self.app.on_exit(); + let mut shared = self.shared.borrow_mut(); shared.painter.destroy(); } diff --git a/crates/eframe/src/native/winit_integration.rs b/crates/eframe/src/native/winit_integration.rs index 2b6c54a67..d85443f37 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -70,6 +70,8 @@ pub trait WinitApp { fn window_id_from_viewport_id(&self, id: ViewportId) -> Option; + fn save(&mut self); + fn save_and_destroy(&mut self); fn run_ui_and_paint( @@ -119,6 +121,9 @@ pub enum EventResult { RepaintAt(WindowId, Instant), + /// Causes a save of the client state when the persistence feature is enabled. + Save, + Exit, } diff --git a/crates/eframe/src/stopwatch.rs b/crates/eframe/src/stopwatch.rs index 9b0136189..e6eabcbdd 100644 --- a/crates/eframe/src/stopwatch.rs +++ b/crates/eframe/src/stopwatch.rs @@ -18,7 +18,7 @@ impl Stopwatch { } pub fn start(&mut self) { - assert!(self.start.is_none()); + assert!(self.start.is_none(), "Stopwatch already running"); self.start = Some(Instant::now()); } @@ -29,7 +29,7 @@ impl Stopwatch { } pub fn resume(&mut self) { - assert!(self.start.is_none()); + assert!(self.start.is_none(), "Stopwatch still running"); self.start = Some(Instant::now()); } diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 6d11069f8..835b7f008 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -59,7 +59,7 @@ impl AppRunner { egui_ctx.options_mut(|o| { // On web by default egui follows the zoom factor of the browser, - // and lets the browser handle the zoom shortscuts. + // and lets the browser handle the zoom shortcuts. // A user can still zoom egui separately by calling [`egui::Context::set_zoom_factor`]. o.zoom_with_keyboard = false; o.zoom_factor = 1.0; @@ -216,6 +216,18 @@ impl AppRunner { let canvas_size = super::canvas_size_in_points(self.canvas(), self.egui_ctx()); let mut raw_input = self.input.new_frame(canvas_size); + if super::DEBUG_RESIZE { + log::info!( + "egui running at canvas size: {}x{}, DPR: {}, zoom_factor: {}. egui size: {}x{} points", + self.canvas().width(), + self.canvas().height(), + super::native_pixels_per_point(), + self.egui_ctx.zoom_factor(), + canvas_size.x, + canvas_size.y, + ); + } + self.app.raw_input_hook(&self.egui_ctx, &mut raw_input); let full_output = self.egui_ctx.run(raw_input, |egui_ctx| { @@ -324,6 +336,9 @@ impl AppRunner { egui::OutputCommand::OpenUrl(open_url) => { super::open_url(&open_url.url, open_url.new_tab); } + egui::OutputCommand::SetPointerPosition(_) => { + // Not supported on the web. + } } } diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 414e5be23..6a1b7b6db 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -1,10 +1,14 @@ +use crate::web::string_from_js_value; + use super::{ button_from_mouse_event, location_hash, modifiers_from_kb_event, modifiers_from_mouse_event, - modifiers_from_wheel_event, pos_from_mouse_event, prefers_color_scheme_dark, primary_touch_pos, - push_touches, text_from_keyboard_event, theme_from_dark_mode, translate_key, AppRunner, - Closure, JsCast, JsValue, WebRunner, + modifiers_from_wheel_event, native_pixels_per_point, pos_from_mouse_event, + prefers_color_scheme_dark, primary_touch_pos, push_touches, text_from_keyboard_event, + theme_from_dark_mode, translate_key, AppRunner, Closure, JsCast, JsValue, WebRunner, + DEBUG_RESIZE, }; -use web_sys::EventTarget; + +use web_sys::{Document, EventTarget, ShadowRoot}; // TODO(emilk): there are more calls to `prevent_default` and `stop_propagation` // than what is probably needed. @@ -215,9 +219,16 @@ fn should_prevent_default_for_key( // * cmd-shift-C (debug tools) // * cmd/ctrl-c/v/x (lest we prevent copy/paste/cut events) - // Prevent ctrl-P from opening the print dialog. Users may want to use it for a command palette. - if egui_key == egui::Key::P && (modifiers.ctrl || modifiers.command || modifiers.mac_cmd) { - return true; + // Prevent cmd/ctrl plus these keys from triggering the default browser action: + let keys = [ + egui::Key::O, // open + egui::Key::P, // print (cmd-P is common for command palette) + egui::Key::S, // save + ]; + for key in keys { + if egui_key == key && (modifiers.ctrl || modifiers.command || modifiers.mac_cmd) { + return true; + } } if egui_key == egui::Key::Space && !runner.text_agent.has_focus() { @@ -363,10 +374,17 @@ fn install_window_events(runner_ref: &WebRunner, window: &EventTarget) -> Result runner.save(); })?; - // NOTE: resize is handled by `ResizeObserver` below + // We want to handle the case of dragging the browser from one monitor to another, + // which can cause the DPR to change without any resize event (e.g. Safari). + install_dpr_change_event(runner_ref)?; + + // No need to subscribe to "resize": we already subscribe to the canvas + // size using a ResizeObserver, and we also subscribe to DPR changes of the monitor. for event_name in &["load", "pagehide", "pageshow"] { runner_ref.add_event_listener(window, event_name, move |_: web_sys::Event, runner| { - // log::debug!("{event_name:?}"); + if DEBUG_RESIZE { + log::debug!("{event_name:?}"); + } runner.needs_repaint.repaint_asap(); })?; } @@ -380,6 +398,48 @@ fn install_window_events(runner_ref: &WebRunner, window: &EventTarget) -> Result Ok(()) } +fn install_dpr_change_event(web_runner: &WebRunner) -> Result<(), JsValue> { + let original_dpr = native_pixels_per_point(); + + let window = web_sys::window().unwrap(); + let Some(media_query_list) = + window.match_media(&format!("(resolution: {original_dpr}dppx)"))? + else { + log::error!( + "Failed to create MediaQueryList: eframe won't be able to detect changes in DPR" + ); + return Ok(()); + }; + + let closure = move |_: web_sys::Event, app_runner: &mut AppRunner, web_runner: &WebRunner| { + let new_dpr = native_pixels_per_point(); + log::debug!("Device Pixel Ratio changed from {original_dpr} to {new_dpr}"); + + if true { + // Explicitly resize canvas to match the new DPR. + // This is a bit ugly, but I haven't found a better way to do it. + let canvas = app_runner.canvas(); + canvas.set_width((canvas.width() as f32 * new_dpr / original_dpr).round() as _); + canvas.set_height((canvas.height() as f32 * new_dpr / original_dpr).round() as _); + log::debug!("Resized canvas to {}x{}", canvas.width(), canvas.height()); + } + + // It may be tempting to call `resize_observer.observe(&canvas)` here, + // but unfortunately this has no effect. + + if let Err(err) = install_dpr_change_event(web_runner) { + log::error!( + "Failed to install DPR change event: {}", + string_from_js_value(&err) + ); + } + }; + + let options = web_sys::AddEventListenerOptions::default(); + options.set_once(true); + web_runner.add_event_listener_ex(&media_query_list, "change", &options, closure) +} + fn install_color_scheme_change_event( runner_ref: &WebRunner, window: &web_sys::Window, @@ -510,10 +570,17 @@ fn install_pointerup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), /// Returns true if the cursor is above the canvas, or if we're dragging something. /// Pass in the position in browser viewport coordinates (usually event.clientX/Y). fn is_interested_in_pointer_event(runner: &AppRunner, pos: egui::Pos2) -> bool { - let document = web_sys::window().unwrap().document().unwrap(); - let is_hovering_canvas = document - .element_from_point(pos.x, pos.y) - .is_some_and(|element| element.eq(runner.canvas())); + let root_node = runner.canvas().get_root_node(); + + let element_at_point = if let Some(document) = root_node.dyn_ref::() { + document.element_from_point(pos.x, pos.y) + } else if let Some(shadow) = root_node.dyn_ref::() { + shadow.element_from_point(pos.x, pos.y) + } else { + None + }; + + let is_hovering_canvas = element_at_point.is_some_and(|element| element.eq(runner.canvas())); let is_pointer_down = runner .egui_ctx() .input(|i| i.pointer.any_down() || i.any_touches()); @@ -813,53 +880,81 @@ fn install_drag_and_drop(runner_ref: &WebRunner, target: &EventTarget) -> Result Ok(()) } -/// Install a `ResizeObserver` to observe changes to the size of the canvas. -/// -/// This is the only way to ensure a canvas size change without an associated window `resize` event -/// actually results in a resize of the canvas. +/// A `ResizeObserver` is used to observe changes to the size of the canvas. /// /// The resize observer is called the by the browser at `observe` time, instead of just on the first actual resize. /// We use that to trigger the first `request_animation_frame` _after_ updating the size of the canvas to the correct dimensions, /// to avoid [#4622](https://github.com/emilk/egui/issues/4622). -pub(crate) fn install_resize_observer(runner_ref: &WebRunner) -> Result<(), JsValue> { - let closure = Closure::wrap(Box::new({ - let runner_ref = runner_ref.clone(); - move |entries: js_sys::Array| { - // Only call the wrapped closure if the egui code has not panicked - if let Some(mut runner_lock) = runner_ref.try_lock() { - let canvas = runner_lock.canvas(); - let (width, height) = match get_display_size(&entries) { - Ok(v) => v, - Err(err) => { - log::error!("{}", super::string_from_js_value(&err)); - return; +pub struct ResizeObserverContext { + observer: web_sys::ResizeObserver, + + // Kept so it is not dropped until we are done with it. + _closure: Closure, +} + +impl Drop for ResizeObserverContext { + fn drop(&mut self) { + self.observer.disconnect(); + } +} + +impl ResizeObserverContext { + pub fn new(runner_ref: &WebRunner) -> Result { + let closure = Closure::wrap(Box::new({ + let runner_ref = runner_ref.clone(); + move |entries: js_sys::Array| { + if DEBUG_RESIZE { + log::info!("ResizeObserverContext callback"); + } + // Only call the wrapped closure if the egui code has not panicked + if let Some(mut runner_lock) = runner_ref.try_lock() { + let canvas = runner_lock.canvas(); + let (width, height) = match get_display_size(&entries) { + Ok(v) => v, + Err(err) => { + log::error!("{}", super::string_from_js_value(&err)); + return; + } + }; + if DEBUG_RESIZE { + log::info!( + "ResizeObserver: new canvas size: {width}x{height}, DPR: {}", + web_sys::window().unwrap().device_pixel_ratio() + ); } - }; - canvas.set_width(width); - canvas.set_height(height); + canvas.set_width(width); + canvas.set_height(height); - // force an immediate repaint - runner_lock.needs_repaint.repaint_asap(); - paint_if_needed(&mut runner_lock); - drop(runner_lock); - // we rely on the resize observer to trigger the first `request_animation_frame`: - if let Err(err) = runner_ref.request_animation_frame() { - log::error!("{}", super::string_from_js_value(&err)); - }; + // force an immediate repaint + runner_lock.needs_repaint.repaint_asap(); + paint_if_needed(&mut runner_lock); + drop(runner_lock); + // we rely on the resize observer to trigger the first `request_animation_frame`: + if let Err(err) = runner_ref.request_animation_frame() { + log::error!("{}", super::string_from_js_value(&err)); + }; + } else { + log::warn!("ResizeObserverContext callback: failed to lock runner"); + } } - } - }) as Box); + }) as Box); - let observer = web_sys::ResizeObserver::new(closure.as_ref().unchecked_ref())?; - let options = web_sys::ResizeObserverOptions::new(); - options.set_box(web_sys::ResizeObserverBoxOptions::ContentBox); - if let Some(runner_lock) = runner_ref.try_lock() { - observer.observe_with_options(runner_lock.canvas(), &options); - drop(runner_lock); - runner_ref.set_resize_observer(observer, closure); + let observer = web_sys::ResizeObserver::new(closure.as_ref().unchecked_ref())?; + + Ok(Self { + observer, + _closure: closure, + }) } - Ok(()) + pub fn observe(&self, canvas: &web_sys::HtmlCanvasElement) { + if DEBUG_RESIZE { + log::info!("Calling observe on canvas…"); + } + let options = web_sys::ResizeObserverOptions::new(); + options.set_box(web_sys::ResizeObserverBoxOptions::ContentBox); + self.observer.observe_with_options(canvas, &options); + } } // Code ported to Rust from: @@ -878,6 +973,10 @@ fn get_display_size(resize_observer_entries: &js_sys::Array) -> Result<(u32, u32 width = size.inline_size(); height = size.block_size(); dpr = 1.0; // no need to apply + + if DEBUG_RESIZE { + // log::info!("devicePixelContentBoxSize {width}x{height}"); + } } else if JsValue::from_str("contentBoxSize").js_in(entry.as_ref()) { let content_box_size = entry.content_box_size(); let idx0 = content_box_size.at(0); @@ -892,6 +991,9 @@ fn get_display_size(resize_observer_entries: &js_sys::Array) -> Result<(u32, u32 width = size.inline_size(); height = size.block_size(); } + if DEBUG_RESIZE { + log::info!("contentBoxSize {width}x{height}"); + } } else { // legacy let content_rect = entry.content_rect(); diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index 911c453f2..c67fa69e6 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -41,7 +41,7 @@ pub(crate) type ActiveWebPainter = web_painter_wgpu::WebPainterWgpu; pub use backend::*; use wasm_bindgen::prelude::*; -use web_sys::MediaQueryList; +use web_sys::{Document, MediaQueryList, Node}; use input::{ button_from_mouse_event, modifiers_from_kb_event, modifiers_from_mouse_event, @@ -51,6 +51,9 @@ use input::{ // ---------------------------------------------------------------------------- +/// Debug browser resizing? +const DEBUG_RESIZE: bool = false; + pub(crate) fn string_from_js_value(value: &JsValue) -> String { value.as_string().unwrap_or_else(|| format!("{value:#?}")) } @@ -61,18 +64,22 @@ pub(crate) fn string_from_js_value(value: &JsValue) -> String { /// - ``/`` with an `href` attribute /// - ``/`