diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 8cd58413..66c17862 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,7 +8,7 @@ assignees: '' --- diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 563a78ed..43e1c02a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,7 +19,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: default - toolchain: 1.64.0 + toolchain: 1.65.0 override: true - name: Install packages (Linux) if: runner.os == 'Linux' @@ -84,7 +84,7 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.64.0 + toolchain: 1.65.0 target: wasm32-unknown-unknown override: true @@ -136,7 +136,7 @@ jobs: - uses: actions/checkout@v2 - uses: EmbarkStudios/cargo-deny-action@v1 with: - rust-version: "1.62.0" + rust-version: "1.65.0" android: name: android @@ -146,10 +146,31 @@ jobs: - uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.64.0 + toolchain: 1.65.0 target: aarch64-linux-android override: true - name: Set up cargo cache uses: Swatinem/rust-cache@v2 - run: cargo check --features wgpu --target aarch64-linux-android working-directory: crates/eframe + + windows: + name: Check Windows + runs-on: windows-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: 1.65.0 + override: true + + - name: Set up cargo cache + uses: Swatinem/rust-cache@v2 + + - name: Check + uses: actions-rs/cargo@v1 + with: + command: check + args: --all-targets --all-features + diff --git a/.gitignore b/.gitignore index bd26ae08..c43b9559 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,7 @@ +.DS_Store **/target +**/target_ra +**/target_wasm /.*.json /.vscode /media/* -.DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json index 3f7a96d5..162d6985 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,32 @@ { "files.insertFinalNewline": true, "editor.formatOnSave": true, - "files.trimTrailingWhitespace": true + "files.trimTrailingWhitespace": true, + "editor.semanticTokenColorCustomizations": { + "rules": { + "*.unsafe:rust": "#eb5046" + } + }, + "files.exclude": { + "target/**": true, + "target_ra/**": true, + }, + // Tell Rust Analyzer to use its own target directory, so we don't need to wait for it to finish wen we want to `cargo run` + "rust-analyzer.checkOnSave.overrideCommand": [ + "cargo", + "cranky", + "--target-dir=target_ra", + "--workspace", + "--message-format=json", + "--all-targets" + ], + "rust-analyzer.cargo.buildScripts.overrideCommand": [ + "cargo", + "check", + "--quiet", + "--target-dir=target_ra", + "--workspace", + "--message-format=json", + "--all-targets" + ], } diff --git a/CHANGELOG.md b/CHANGELOG.md index dc4785b3..0ae6e238 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,55 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG ## Unreleased +* โš ๏ธ BREAKING: `egui::Context` now use closures for locking ([#2625](https://github.com/emilk/egui/pull/2625)): + * `ctx.input().key_pressed(Key::A)` -> `ctx.input(|i| i.key_pressed(Key::A))` + * `ui.memory().toggle_popup(popup_id)` -> `ui.memory_mut(|mem| mem.toggle_popup(popup_id))` + +### Added โญ +* Add `Response::drag_started_by` and `Response::drag_released_by` for convenience, similar to `dragged` and `dragged_by` ([#2507](https://github.com/emilk/egui/pull/2507)). +* Add `PointerState::*_pressed` to check if the given button was pressed in this frame ([#2507](https://github.com/emilk/egui/pull/2507)). +* `Event::Key` now has a `repeat` field that is set to `true` if the event was the result of a key-repeat ([#2435](https://github.com/emilk/egui/pull/2435)). +* Add `Slider::drag_value_speed`, which lets you ask for finer precision when dragging the slider value rather than the actual slider. +* Add `Memory::any_popup_open`, which returns true if any popup is currently open ([#2464](https://github.com/emilk/egui/pull/2464)). +* Add `Plot::clamp_grid` to only show grid where there is data ([#2480](https://github.com/emilk/egui/pull/2480)). +* Add `ScrollArea::drag_to_scroll` if you want to turn off that feature. +* Add `Response::on_hover_and_drag_cursor`. +* Add `Window::default_open` ([#2539](https://github.com/emilk/egui/pull/2539)). +* Add `ProgressBar::fill` if you want to set the fill color manually. ([#2618](https://github.com/emilk/egui/pull/2618)). +* Add `Button::rounding` to enable round buttons ([#2616](https://github.com/emilk/egui/pull/2616)). +* Add `WidgetVisuals::optional_bg_color` - set it to `Color32::TRANSPARENT` to hide button backgrounds ([#2621](https://github.com/emilk/egui/pull/2621)). +* Add `Context::screen_rect` and `Context::set_cursor_icon` ([#2625](https://github.com/emilk/egui/pull/2625)). +* You can turn off the vertical line left of indented regions with `Visuals::indent_has_left_vline` ([#2636](https://github.com/emilk/egui/pull/2636)). +* Add `Response.highlight` to highlight a widget ([#2632](https://github.com/emilk/egui/pull/2632)). +* Add `Separator::grow` and `Separator::shrink` ([#2665](https://github.com/emilk/egui/pull/2665)). + +### Changed ๐Ÿ”ง +* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)). +* Improved the algorithm for picking the number of decimals to show when hovering values in the `Plot`. +* Default `ComboBox` is now controlled with `Spacing::combo_width` ([#2621](https://github.com/emilk/egui/pull/2621)). +* `DragValue` and `Slider` now use the proportional font ([#2638](https://github.com/emilk/egui/pull/2638)). +* `ScrollArea` is less aggressive about clipping its contents ([#2665](https://github.com/emilk/egui/pull/2665)). + +### Fixed ๐Ÿ› +* Trigger `PointerEvent::Released` for drags ([#2507](https://github.com/emilk/egui/pull/2507)). +* Expose `TextEdit`'s multiline flag to AccessKit ([#2448](https://github.com/emilk/egui/pull/2448)). +* Don't render `\r` (Carriage Return) ([#2452](https://github.com/emilk/egui/pull/2452)). +* The `button_padding` style option works closer as expected with image+text buttons now ([#2510](https://github.com/emilk/egui/pull/2510)). +* Fixed rendering of `โ€ฆ` (ellipsis). +* Menus are now moved to fit on the screen. + + +## 0.20.1 - 2022-12-11 - Fix key-repeat +### Changed ๐Ÿ”ง +* `InputState`: all press functions again include key repeats (like in egui 0.19) ([#2429](https://github.com/emilk/egui/pull/2429)). +* Improve the look of thin white lines ([#2437](https://github.com/emilk/egui/pull/2437)). + +### Fixed ๐Ÿ› +* Fix key-repeats for `TextEdit`, `Slider`s, etc ([#2429](https://github.com/emilk/egui/pull/2429)). + + +## 0.20.0 - 2022-12-08 - AccessKit, prettier text, overlapping widgets +* MSRV (Minimum Supported Rust Version) is now `1.65.0` ([#2314](https://github.com/emilk/egui/pull/2314)). * โš ๏ธ BREAKING: egui now expects integrations to do all color blending in gamma space ([#2071](https://github.com/emilk/egui/pull/2071)). * โš ๏ธ BREAKING: if you have overlapping interactive widgets, only the top widget (last added) will be interactive ([#2244](https://github.com/emilk/egui/pull/2244)). @@ -17,19 +66,42 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG * Added `Key::Minus` and `Key::Equals` ([#2239](https://github.com/emilk/egui/pull/2239)). * Added `egui::gui_zoom` module with helpers for scaling the whole GUI of an app ([#2239](https://github.com/emilk/egui/pull/2239)). * You can now put one interactive widget on top of another, and only one will get interaction at a time ([#2244](https://github.com/emilk/egui/pull/2244)). -* Added `ui.centered`. -* Added `Area::constrain` and `Window::constrain` which constrains area to the screen bounds. ([#2270](https://github.com/emilk/egui/pull/2270)). -* Added `Area::pivot` and `Window::pivot` which controls what part of the window to position. ([#2303](https://github.com/emilk/egui/pull/2303)). +* Added `spacing.menu_margin` for customizing menu spacing ([#2036](https://github.com/emilk/egui/pull/2036)) +* Added possibility to enable text wrap for the selected text of `egui::ComboBox` ([#2272](https://github.com/emilk/egui/pull/2272)) +* Added `Area::constrain` and `Window::constrain` which constrains area to the screen bounds ([#2270](https://github.com/emilk/egui/pull/2270)). +* Added `Area::pivot` and `Window::pivot` which controls what part of the window to position ([#2303](https://github.com/emilk/egui/pull/2303)). +* Added support for [thin space](https://en.wikipedia.org/wiki/Thin_space). +* Added optional integration with [AccessKit](https://accesskit.dev/) for implementing platform accessibility APIs ([#2294](https://github.com/emilk/egui/pull/2294)). +* Added `panel_fill`, `window_fill` and `window_stroke` to `Visuals` for your theming pleasure ([#2406](https://github.com/emilk/egui/pull/2406)). +* Plots: + * Allow linking plot cursors ([#1722](https://github.com/emilk/egui/pull/1722)). + * Added `Plot::auto_bounds_x/y` and `Plot::reset` ([#2029](https://github.com/emilk/egui/pull/2029)). + * Added `PlotUi::translate_bounds` ([#2145](https://github.com/emilk/egui/pull/2145)). + * Added `PlotUi::set_plot_bounds` ([#2320](https://github.com/emilk/egui/pull/2320)). + * Added `PlotUi::plot_secondary_clicked` ([#2318](https://github.com/emilk/egui/pull/2318)). ### Changed ๐Ÿ”ง * Panels always have a separator line, but no stroke on other sides. Their spacing has also changed slightly ([#2261](https://github.com/emilk/egui/pull/2261)). * Tooltips are only shown when mouse pointer is still ([#2263](https://github.com/emilk/egui/pull/2263)). +* Make it slightly easier to click buttons ([#2304](https://github.com/emilk/egui/pull/2304)). +* `egui::color` has been renamed `egui::ecolor` ([#2399](https://github.com/emilk/egui/pull/2399)). ### Fixed ๐Ÿ› * โš ๏ธ BREAKING: Fix text being too small ([#2069](https://github.com/emilk/egui/pull/2069)). +* Improve mixed CJK/Latin line-breaking ([#1986](https://github.com/emilk/egui/pull/1986)). * Improved text rendering ([#2071](https://github.com/emilk/egui/pull/2071)). +* Constrain menu popups to the screen ([#2191](https://github.com/emilk/egui/pull/2191)). * Less jitter when calling `Context::set_pixels_per_point` ([#2239](https://github.com/emilk/egui/pull/2239)). * Fixed popups and color edit going outside the screen. +* Fixed keyboard support in `DragValue` ([#2342](https://github.com/emilk/egui/pull/2342)). +* If you nest `ScrollAreas` inside each other, the inner area will now move its scroll bar so it is always visible ([#2371](https://github.com/emilk/egui/pull/2371)). +* Ignore key-repeats for `input.key_pressed` ([#2334](https://github.com/emilk/egui/pull/2334), [#2389](https://github.com/emilk/egui/pull/2389)). +* Fixed issue with calling `set_pixels_per_point` each frame ([#2352](https://github.com/emilk/egui/pull/2352)). +* Fix bug in `ScrollArea::show_rows` ([#2258](https://github.com/emilk/egui/pull/2258)). +* Fix bug in `plot::Line::fill` ([#2275](https://github.com/emilk/egui/pull/2275)). +* Only emit `changed` events in `radio_value` and `selectable_value` if the value actually changed ([#2343](https://github.com/emilk/egui/pull/2343)). +* Fixed sizing bug in `Grid` ([#2384](https://github.com/emilk/egui/pull/2384)). +* `ComboBox::width` now correctly sets the outer width ([#2406](https://github.com/emilk/egui/pull/2406)). ## 0.19.0 - 2022-08-20 @@ -43,7 +115,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG * You can now specify a texture filter for your textures ([#1636](https://github.com/emilk/egui/pull/1636)). * Added functions keys in `egui::Key` ([#1665](https://github.com/emilk/egui/pull/1665)). * Added support for using `PaintCallback` shapes with the WGPU backend ([#1684](https://github.com/emilk/egui/pull/1684)). -* Added `Contex::request_repaint_after` ([#1694](https://github.com/emilk/egui/pull/1694)). +* Added `Context::request_repaint_after` ([#1694](https://github.com/emilk/egui/pull/1694)). * `ctrl-h` now acts like backspace in `TextEdit` ([#1812](https://github.com/emilk/egui/pull/1812)). * Added `custom_formatter` method for `Slider` and `DragValue` ([#1851](https://github.com/emilk/egui/issues/1851)). * Added `RawInput::has_focus` which backends can set to indicate whether the UI as a whole has the keyboard focus ([#1859](https://github.com/emilk/egui/pull/1859)). diff --git a/Cargo.lock b/Cargo.lock index 99b1d4b0..6a7cb99d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.16" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "846ffacb9d0c8b879ef9e565b59e18fb76d6a61013e5bd24ecc659864e6b1a1f" +checksum = "fe21446ad43aa56417a767f3e2f3d7c4ca522904de1dd640529a76e9c5c3b33c" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,15 +14,94 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.5" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13739d7177fbd22bb0ed28badfff9f372f8bef46c863db4e1c6248f6b223b6e" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "accesskit" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3083ac5a97521e35388ca80cf365b6be5210962cc59f11ee238cd92ac2fa9524" +dependencies = [ + "enumset", + "kurbo", + "serde", +] + +[[package]] +name = "accesskit_consumer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f47393f706a2d2f9d1ebd109351f886afd256a09d2308861a6dec0853a625e2" +dependencies = [ + "accesskit", + "parking_lot", +] + +[[package]] +name = "accesskit_macos" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fabafb94d8a4dd6b20fe4112f943756ff8dc9778e3d742fb5478bf7f000a3282" +dependencies = [ + "accesskit", + "accesskit_consumer", + "objc2", + "once_cell", + "parking_lot", +] + +[[package]] +name = "accesskit_unix" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf322ecf51ac3fb9d3016382e5515122650d3fe70afe544322215e9a54f68f" +dependencies = [ + "accesskit", + "accesskit_consumer", + "async-channel", + "atspi", + "futures-lite", + "parking_lot", + "serde", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "620160ad7d0aec2b4ae487bc8fbf0367982651d27bf8908a49d03548cfce73ec" +dependencies = [ + "accesskit", + "accesskit_consumer", + "arrayvec", + "once_cell", + "parking_lot", + "paste", + "windows 0.42.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad401ccee3adde31edbbf7e8c7dd3fcb3fb916f7e519c135608b6b5231d633d4" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "parking_lot", + "winit", +] [[package]] name = "addr2line" -version = "0.17.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" dependencies = [ "gimli", ] @@ -33,18 +112,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "adler32" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" - -[[package]] -name = "ahash" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" - [[package]] name = "ahash" version = "0.7.6" @@ -58,12 +125,11 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.1" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464b3811b747f8f7ebc8849c9c728c39f6ac98a055edad93baf9eb330e3f8f9d" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" dependencies = [ "cfg-if", - "getrandom", "once_cell", "serde", "version_check", @@ -71,18 +137,42 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "0.7.18" +version = "0.7.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" dependencies = [ "memchr", ] [[package]] -name = "android_system_properties" -version = "0.1.4" +name = "android-activity" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7ed72e1635e121ca3e79420540282af22da58be50de153d36f81ddc6b83aa9e" +checksum = "4165a1aef703232031b40a6e8908c2f9e314d495f11aa7f98db75d39a497cc6a" +dependencies = [ + "android-properties", + "bitflags", + "cc", + "jni-sys", + "libc", + "log", + "ndk", + "ndk-context", + "ndk-sys", + "num_enum", +] + +[[package]] +name = "android-properties" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] @@ -93,20 +183,11 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anyhow" -version = "1.0.62" +version = "1.0.68" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305" +checksum = "2cb2f989d18dd141ab8ae82f64d1a8cdd37e0840f73a406896cf5e99502fab61" [[package]] name = "approx" @@ -141,12 +222,6 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - [[package]] name = "arrayvec" version = "0.7.2" @@ -155,18 +230,18 @@ checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "ash" -version = "0.37.0+1.3.209" +version = "0.37.2+1.3.238" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006ca68e0f2b03f22d6fa9f2860f85aed430d257fec20f8879b2145e7c7ae1a6" +checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03" dependencies = [ "libloading", ] [[package]] name = "async-broadcast" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d26004fe83b2d1cd3a97609b21e39f9a31535822210fe83205d2ce48866ea61" +checksum = "1b19760fa2b7301cf235360ffd6d3558b1ed4249edd16d6cca8d690cee265b95" dependencies = [ "event-listener", "futures-core", @@ -175,9 +250,9 @@ dependencies = [ [[package]] name = "async-channel" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14485364214912d3b19cc3435dde4df66065127f05fa0d75c712f36f12c2f28" +checksum = "cf46fee83e5ccffc220104713af3292ff9bc7c64c7de289f66dae8e38d826833" dependencies = [ "concurrent-queue", "event-listener", @@ -186,52 +261,53 @@ dependencies = [ [[package]] name = "async-executor" -version = "1.4.1" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +checksum = "17adb73da160dfb475c183343c8cccd80721ea5a605d3eb57125f0a7b7a92d0b" dependencies = [ + "async-lock", "async-task", "concurrent-queue", "fastrand", "futures-lite", - "once_cell", "slab", ] [[package]] name = "async-io" -version = "1.8.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab006897723d9352f63e2b13047177c3982d8d79709d713ce7747a8f19fd1b0" +checksum = "8c374dda1ed3e7d8f0d9ba58715f924862c63eae6849c92d3a18e7fbde9e2794" dependencies = [ + "async-lock", "autocfg", "concurrent-queue", "futures-lite", "libc", "log", - "once_cell", "parking", "polling", "slab", "socket2", "waker-fn", - "winapi", + "windows-sys 0.42.0", ] [[package]] name = "async-lock" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e97a171d191782fba31bb902b14ad94e24a68145032b7eedf871ab0bc0d077b6" +checksum = "c8101efe8695a6c17e02911402145357e718ac92d3ff88ae8419e84b1707b685" dependencies = [ "event-listener", + "futures-lite", ] [[package]] name = "async-recursion" -version = "0.3.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7d78656ba01f1b93024b7c3a0467f1608e4be67d725749fdcd7d2c7678fd7a2" +checksum = "3b015a331cc64ebd1774ba119538573603427eaace0a1950c423ab971f903796" dependencies = [ "proc-macro2", "quote", @@ -246,9 +322,9 @@ checksum = "7a40729d2133846d9ed0ea60a8b9541bccddab49cd30f0715a1da672fe9a2524" [[package]] name = "async-trait" -version = "0.1.57" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76464446b8bc32758d7e88ee1a804d9914cd9b1cb264c029899680b0be29826f" +checksum = "1cd7fce9ba8c3c042128ce72d8b2ddbf3a05747efb67ea0313c635e10bda47a2" dependencies = [ "proc-macro2", "quote", @@ -269,9 +345,41 @@ dependencies = [ [[package]] name = "atomic_refcell" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73b5e5f48b927f04e952dedc932f31995a65a0bf65ec971c74436e51bf6e970d" +checksum = "857253367827bd9d0fd973f0ef15506a96e79e41b0ad7aa691203a4e3214f6c8" + +[[package]] +name = "atspi" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab84c09a770065868da0d713f1f4b35af85d96530a868f1c1a6c249178379187" +dependencies = [ + "async-recursion", + "async-trait", + "atspi-macros", + "enumflags2", + "futures-lite", + "serde", + "tracing", + "zbus", + "zbus_names", +] + +[[package]] +name = "atspi-macros" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ebc5a6f61f6996eca56a4cece7b3fe7da3b86f0473c7b71ab44e229f3acce4" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "zbus", + "zbus_names", + "zvariant", +] [[package]] name = "atty" @@ -292,9 +400,9 @@ checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.66" +version = "0.3.67" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab84319d616cfb654d03394f38ab7e6f0919e181b1b57e1fd15e7fb4077d9a7" +checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" dependencies = [ "addr2line", "cc", @@ -307,9 +415,9 @@ dependencies = [ [[package]] name = "base64" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" [[package]] name = "bincode" @@ -322,9 +430,9 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.61.0" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" +checksum = "36d860121800b2a9a94f9b5604b332d5cffb234ce17609ea479d723dbc9d3885" dependencies = [ "bitflags", "cexpr", @@ -370,25 +478,53 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] -name = "bumpalo" -version = "3.11.0" +name = "block-buffer" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ad822118d20d2c234f427000d5acc36eabe1e29a348c89b63dd60b13f28e5d" +checksum = "69cce20737498f97b993470a6e536b8523f0af7892a4f928cceb1ac5e52ebe7e" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-sys" +version = "0.1.0-beta.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa55741ee90902547802152aaf3f8e5248aab7e21468089560d4c8840561146" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.2.0-alpha.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dd9e63c1744f755c2f60332b88de39d341e5e86239014ad839bd71c106dec42" +dependencies = [ + "block-sys", + "objc2-encode", +] + +[[package]] +name = "bumpalo" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" [[package]] name = "bytemuck" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f5715e491b5a1598fc2bef5a606847b5dc1d48ea625bd3c02c00de8285591da" +checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9e1f5fa78f69496407a27ae9ed989e3c3b072310286f5ef385525e4cbc24a9" +checksum = "1aca418a974d83d40a0c1f0c5cba6ff4bc28d8df099109ca459a2118d40b6322" dependencies = [ "proc-macro2", "quote", @@ -403,15 +539,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "bytes" -version = "1.2.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db" - -[[package]] -name = "cache-padded" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1db59621ec70f09c5e9b597b220c7a2b43611f4710dc03ceb8748637775692c" +checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" [[package]] name = "cairo-sys-rs" @@ -425,12 +555,12 @@ dependencies = [ [[package]] name = "calloop" -version = "0.10.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a22a6a8f622f797120d452c630b0ab12e1331a1a753e2039ce7868d4ac77b4ee" +checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192" dependencies = [ "log", - "nix 0.24.2", + "nix 0.25.1", "slotmap", "thiserror", "vec_map", @@ -444,9 +574,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.0.73" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" +checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" dependencies = [ "jobserver", ] @@ -468,9 +598,9 @@ dependencies = [ [[package]] name = "cfg-expr" -version = "0.10.3" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aacacf4d96c24b2ad6eb8ee6df040e4f27b0d0b39a5710c30091baa830485db" +checksum = "b0357a6402b295ca3a86bc148e84df46c02e41f41fef186bda662557ef6328aa" dependencies = [ "smallvec", ] @@ -508,25 +638,19 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.22" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfd4d1b31faaa3a89d7934dbded3111da0d2ef28e3ebccdb4f0179f5929d1ef1" +checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" dependencies = [ "iana-time-zone", "js-sys", "num-integer", "num-traits", - "time 0.1.44", + "time 0.1.45", "wasm-bindgen", "winapi", ] -[[package]] -name = "chunked_transfer" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff857943da45f546682664a79488be82e69e43c1a7a2307679ab9afb3a66d2e" - [[package]] name = "ciborium" version = "0.2.0" @@ -562,9 +686,9 @@ checksum = "7a0e87cdf78571d9fbeff16861c37a006cd718d2433dc6d5b80beaae367d899a" [[package]] name = "clang-sys" -version = "1.3.3" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a050e2153c5be08febd6734e29298e844fdb0fa21aeddd63b4eb7baa106c69b" +checksum = "fa2e27ae6ab525c3d369ded447057bca5438d86dc3a68f6faafb8269ba82ebf3" dependencies = [ "glob", "libc", @@ -573,9 +697,9 @@ dependencies = [ [[package]] name = "clap" -version = "3.2.17" +version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29e724a68d9319343bb3328c9cc2dfde263f4b3142ee1059a9980580171c954b" +checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ "bitflags", "clap_lex", @@ -594,36 +718,27 @@ dependencies = [ [[package]] name = "clipboard-win" -version = "4.4.2" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4ab1b92798304eedc095b53942963240037c0516452cb11aeba709d420b2219" +checksum = "7191c27c2357d9b7ef96baac1773290d4ca63b24205b82a3fd8a0637afcf0362" dependencies = [ "error-code", "str-buf", "winapi", ] -[[package]] -name = "cmake" -version = "0.1.48" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8ad8cef104ac57b68b89df3208164d228503abbdce70f6880ffa3d970e7443a" -dependencies = [ - "cc", -] - [[package]] name = "cocoa" -version = "0.24.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f63902e9223530efb4e26ccd0cf55ec30d592d3b42e21a28defc42a9586e832" +checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ "bitflags", "block", "cocoa-foundation", "core-foundation", "core-graphics", - "foreign-types 0.3.2", + "foreign-types", "libc", "objc", ] @@ -638,7 +753,7 @@ dependencies = [ "block", "core-foundation", "core-graphics-types", - "foreign-types 0.3.2", + "foreign-types", "libc", "objc", ] @@ -665,6 +780,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "com-rs" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf43edc576402991846b093a7ca18a3477e0ef9c588cde84964b5d3e43016642" + [[package]] name = "combine" version = "4.6.6" @@ -677,11 +798,11 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "1.2.4" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4780a44ab5696ea9e28294517f1fffb421a83a25af521333c838635509db9c" +checksum = "c278839b831783b70278b14df4d45e1beb1aad306c07bb796637de9a0e323e8e" dependencies = [ - "cache-padded", + "crossbeam-utils", ] [[package]] @@ -726,7 +847,7 @@ dependencies = [ "bitflags", "core-foundation", "core-graphics-types", - "foreign-types 0.3.2", + "foreign-types", "libc", ] @@ -738,19 +859,16 @@ checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ "bitflags", "core-foundation", - "foreign-types 0.3.2", + "foreign-types", "libc", ] [[package]] -name = "core-text" -version = "19.2.0" +name = "cpufeatures" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320" dependencies = [ - "core-foundation", - "core-graphics", - "foreign-types 0.3.2", "libc", ] @@ -809,35 +927,21 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.11" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51887d4adc7b564537b15adcfb307936f8075dfcd5f00dde9a9f1d29383682bc" +checksum = "4fb766fa798726286dbbb842f174001dab8abc7b627a1dd86e0b7222a95d929f" dependencies = [ "cfg-if", - "once_cell", ] [[package]] -name = "crossfont" -version = "0.5.0" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f66b1c1979c4362323f03ab6bf7fb522902bfc418e0c37319ab347f9561d980f" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "cocoa", - "core-foundation", - "core-foundation-sys", - "core-graphics", - "core-text", - "dwrote", - "foreign-types 0.5.0", - "freetype-rs", - "libc", - "log", - "objc", - "once_cell", - "pkg-config", - "servo-fontconfig", - "winapi", + "generic-array", + "typenum", ] [[package]] @@ -852,7 +956,7 @@ version = "0.1.0" dependencies = [ "eframe", "egui_glow", - "glow", + "glow 0.11.2", ] [[package]] @@ -862,7 +966,7 @@ dependencies = [ "console_error_panic_hook", "eframe", "egui_glow", - "glow", + "glow 0.11.2", "three-d", "wasm-bindgen", "wasm-bindgen-futures", @@ -890,10 +994,54 @@ dependencies = [ ] [[package]] -name = "d3d12" -version = "0.5.0" +name = "cxx" +version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827914e1f53b1e0e025ecd3d967a7836b7bcb54520f90e21ef8df7b4d88a2759" +checksum = "bc831ee6a32dd495436e317595e639a587aa9907bef96fe6e6abc290ab6204e9" +dependencies = [ + "cc", + "cxxbridge-flags", + "cxxbridge-macro", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94331d54f1b1a8895cd81049f7eaaaef9d05a7dcb4d1fd08bf3ff0806246789d" +dependencies = [ + "cc", + "codespan-reporting", + "once_cell", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48dcd35ba14ca9b40d6e4b4b39961f23d835dbb8eed74565ded361d93e1feb8a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bbeb29798b407ccd82a3324ade1a7286e0d29851475990b612670f6f5124d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "d3d12" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f0de2f5a8e7bd4a9eec0e3c781992a4ce1724f68aec7d7a3715344de8b39da" dependencies = [ "bitflags", "libloading", @@ -902,9 +1050,9 @@ dependencies = [ [[package]] name = "dark-light" -version = "0.2.2" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b83576e2eee2d9cdaa8d08812ae59cbfe1b5ac7ac5ac4b8400303c6148a88c1" +checksum = "a62007a65515b3cd88c733dd3464431f05d2ad066999a824259d8edc3cf6f645" dependencies = [ "dconf_rs", "detect-desktop-environment", @@ -923,8 +1071,18 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0dd3cd20dc6b5a876612a6e5accfe7f3dd883db6d07acfbf14c128f61550dfa" +dependencies = [ + "darling_core 0.14.2", + "darling_macro 0.14.2", ] [[package]] @@ -941,25 +1099,46 @@ dependencies = [ "syn", ] +[[package]] +name = "darling_core" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a784d2ccaf7c98501746bf0be29b2022ba41fd62a2e622af997a03e9f972859f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", + "quote", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7618812407e9402654622dd402b0a89dff9ba93badd6540781526117b92aab7e" +dependencies = [ + "darling_core 0.14.2", "quote", "syn", ] [[package]] name = "data-url" -version = "0.1.1" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30bfce702bcfa94e906ef82421f2c0e61c076ad76030c16ee5d2e9a32fe193" -dependencies = [ - "matches", -] +checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5" [[package]] name = "dconf_rs" @@ -967,15 +1146,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" -[[package]] -name = "deflate" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f" -dependencies = [ - "adler32", -] - [[package]] name = "derivative" version = "2.2.0" @@ -993,6 +1163,16 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" +[[package]] +name = "digest" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "directories-next" version = "2.0.0" @@ -1040,17 +1220,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" -[[package]] -name = "displaydoc" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf95dc3f046b9da4f2d51833c0d3547d8564ef6910f5c1ed130306a75b92886" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "dlib" version = "0.5.0" @@ -1062,18 +1231,18 @@ dependencies = [ [[package]] name = "dlv-list" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68df3f2b690c1b86e65ef7830956aededf3cb0a16f898f79b9a6f421a7b6211b" -dependencies = [ - "rand", -] +checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] name = "document-features" -version = "0.2.3" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d99bbe945402eb228b85cdfc7020cf7422574976861c642b28255d0a49606d79" +checksum = "e493c573fce17f00dcab13b6ac057994f3ce17d1af4dc39bfd482b83c6eb6157" +dependencies = [ + "litrs", +] [[package]] name = "downcast-rs" @@ -1092,20 +1261,6 @@ dependencies = [ "poll-promise", ] -[[package]] -name = "dwrote" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b" -dependencies = [ - "lazy_static", - "libc", - "serde", - "serde_derive", - "winapi", - "wio", -] - [[package]] name = "dyn-clonable" version = "0.9.0" @@ -1129,13 +1284,24 @@ dependencies = [ [[package]] name = "dyn-clone" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f94fa09c2aeea5b8839e414b7b841bf429fd25b9c522116ac97ee87856d88b2" +checksum = "c9b0705efd4599c15a38151f4721f7bc388306f61084d3bfd50bd07fbca5cb60" + +[[package]] +name = "ecolor" +version = "0.20.0" +dependencies = [ + "bytemuck", + "cint", + "color-hex", + "document-features", + "serde", +] [[package]] name = "eframe" -version = "0.19.0" +version = "0.20.1" dependencies = [ "bytemuck", "dark-light", @@ -1145,13 +1311,17 @@ dependencies = [ "egui-wgpu", "egui-winit", "egui_glow", - "glow", + "glow 0.11.2", "glutin", + "image", "js-sys", "percent-encoding", + "pollster", "puffin", + "raw-window-handle", "ron", "serde", + "thiserror", "tracing", "tts", "wasm-bindgen", @@ -1163,9 +1333,10 @@ dependencies = [ [[package]] name = "egui" -version = "0.19.0" +version = "0.20.1" dependencies = [ - "ahash 0.8.1", + "accesskit", + "ahash 0.8.3", "document-features", "epaint", "nohash-hasher", @@ -1176,12 +1347,11 @@ dependencies = [ [[package]] name = "egui-wgpu" -version = "0.19.0" +version = "0.20.0" dependencies = [ "bytemuck", "document-features", - "egui", - "pollster", + "epaint", "puffin", "tracing", "type-map", @@ -1191,8 +1361,10 @@ dependencies = [ [[package]] name = "egui-winit" -version = "0.19.0" +version = "0.20.1" dependencies = [ + "accesskit_winit", + "android-activity", "arboard", "document-features", "egui", @@ -1201,14 +1373,13 @@ dependencies = [ "serde", "smithay-clipboard", "tracing", - "tts", "webbrowser", "winit", ] [[package]] name = "egui_demo_app" -version = "0.19.0" +version = "0.20.0" dependencies = [ "bytemuck", "chrono", @@ -1229,7 +1400,7 @@ dependencies = [ [[package]] name = "egui_demo_lib" -version = "0.19.0" +version = "0.20.0" dependencies = [ "chrono", "criterion", @@ -1245,7 +1416,7 @@ dependencies = [ [[package]] name = "egui_extras" -version = "0.19.0" +version = "0.20.0" dependencies = [ "chrono", "document-features", @@ -1253,37 +1424,24 @@ dependencies = [ "image", "resvg", "serde", - "tiny-skia 0.6.6", + "tiny-skia", "tracing", "usvg", ] -[[package]] -name = "egui_glium" -version = "0.19.0" -dependencies = [ - "ahash 0.8.1", - "bytemuck", - "document-features", - "egui", - "egui-winit", - "egui_demo_lib", - "glium", - "image", -] - [[package]] name = "egui_glow" -version = "0.19.0" +version = "0.20.1" dependencies = [ "bytemuck", "document-features", "egui", "egui-winit", - "glow", + "glow 0.11.2", "glutin", "memoffset", "puffin", + "raw-window-handle", "tracing", "wasm-bindgen", "web-sys", @@ -1304,13 +1462,13 @@ dependencies = [ [[package]] name = "either" -version = "1.8.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90e5c1c8368803113bf0c9584fc495a58b86dc8a29edbf8fe877d21d9507e797" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" [[package]] name = "emath" -version = "0.19.0" +version = "0.20.0" dependencies = [ "bytemuck", "document-features", @@ -1320,9 +1478,9 @@ dependencies = [ [[package]] name = "enum-map" -version = "2.4.1" +version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5a56d54c8dd9b3ad34752ed197a4eb2a6601bc010808eb097a04a58ae4c43e1" +checksum = "50c25992259941eb7e57b936157961b217a4fc8597829ddef0596d6c3cd86e1a" dependencies = [ "enum-map-derive", "serde", @@ -1330,9 +1488,9 @@ dependencies = [ [[package]] name = "enum-map-derive" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9045e2676cd5af83c3b167d917b0a5c90a4d8e266e2683d6631b235c457fc27" +checksum = "2a4da76b3b6116d758c7ba93f7ec6a35d2e2cf24feda76c6e38a375f4d5c59f2" dependencies = [ "proc-macro2", "quote", @@ -1360,19 +1518,40 @@ dependencies = [ "syn", ] +[[package]] +name = "enumset" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19be8061a06ab6f3a6cf21106c873578bf01bd42ad15e0311a9c76161cb1c753" +dependencies = [ + "enumset_derive", + "serde", +] + +[[package]] +name = "enumset_derive" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03e7b551eba279bf0fa88b83a46330168c1560a52a94f5126f892f0b364ab3e0" +dependencies = [ + "darling 0.14.2", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "epaint" -version = "0.19.0" +version = "0.20.0" dependencies = [ "ab_glyph", - "ahash 0.8.1", + "ahash 0.8.3", "atomic_refcell", "backtrace", "bytemuck", - "cint", - "color-hex", "criterion", "document-features", + "ecolor", "emath", "nohash-hasher", "parking_lot", @@ -1395,16 +1574,6 @@ version = "2.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" -[[package]] -name = "expat-sys" -version = "2.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658f19728920138342f68408b7cf7644d90d4784353d8ebc32e7e8663dbe45fa" -dependencies = [ - "cmake", - "pkg-config", -] - [[package]] name = "fancy-regex" version = "0.7.1" @@ -1434,9 +1603,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.24" +version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", "miniz_oxide", @@ -1454,55 +1623,13 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -[[package]] -name = "fontconfig-parser" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cea2adebf32a9b104b8ffb308b5fb3b456f04cc76c294c3c85025c8a5d75f4" -dependencies = [ - "roxmltree", -] - -[[package]] -name = "fontdb" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122fa73a5566372f9df09768a16e8e3dad7ad18abe07835f1f0b71f84078ba4c" -dependencies = [ - "fontconfig-parser", - "log", - "memmap2", - "ttf-parser", -] - [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8469d0d40519bc608ec6863f1cc88f3f1deee15913f2f3b3e573d81ed38cccc" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "foreign-types-shared", ] [[package]] @@ -1511,55 +1638,26 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - [[package]] name = "form_urlencoded" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" dependencies = [ - "matches", "percent-encoding", ] -[[package]] -name = "freetype-rs" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74eadec9d0a5c28c54bb9882e54787275152a4e36ce206b45d7451384e5bf5fb" -dependencies = [ - "bitflags", - "freetype-sys", - "libc", -] - -[[package]] -name = "freetype-sys" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" -dependencies = [ - "cmake", - "libc", - "pkg-config", -] - [[package]] name = "futures-core" -version = "0.3.23" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2acedae88d38235936c3922476b10fced7b2b68136f5e3c03c2d5be348a1115" +checksum = "ec90ff4d0fe1f57d600049061dc6bb68ed03c7d2fbd697274c41805dcb3f8608" [[package]] name = "futures-io" -version = "0.3.23" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93a66fc6d035a26a3ae255a6d2bca35eda63ae4c5512bef54449113f7a1228e5" +checksum = "bfb8371b6fb2aeb2d280374607aeabfc99d95c72edfe51692e42d3d7f0d08531" [[package]] name = "futures-lite" @@ -1578,21 +1676,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.23" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0bae1fe9752cf7fd9b0064c674ae63f97b37bc714d745cbde0afb7ec4e6765" +checksum = "f310820bb3e8cfd46c80db4d7fb8353e15dfff853a127158425f31e0be6c8364" [[package]] name = "futures-task" -version = "0.3.23" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "842fc63b931f4056a24d59de13fb1272134ce261816e063e634ad0c15cdc5306" +checksum = "dcf79a1bf610b10f42aea489289c5a2c478a786509693b80cd39c44ccd936366" [[package]] name = "futures-util" -version = "0.3.23" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" +checksum = "9c1d6de3acfef38d2be4b1f543f553131788603495be83da675e180c8d6b7bd1" dependencies = [ "futures-core", "futures-sink", @@ -1641,6 +1739,16 @@ dependencies = [ "system-deps", ] +[[package]] +name = "generic-array" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "gethostname" version = "0.2.3" @@ -1653,32 +1761,20 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.7" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31" dependencies = [ "cfg-if", - "js-sys", "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "wasm-bindgen", -] - -[[package]] -name = "gif" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3edd93c6756b4dfaf2709eafcc345ba2636565295c198a9cfbf75fa5e3e00b06" -dependencies = [ - "color_quant", - "weezl", ] [[package]] name = "gimli" -version = "0.26.2" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" +checksum = "221996f774192f0f718773def8201c4ae31f02616a54ccfc2d358bb0e5cefdec" [[package]] name = "gio-sys" @@ -1714,27 +1810,11 @@ dependencies = [ "system-deps", ] -[[package]] -name = "glium" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2766728ecb86014b91d3d687614b32d65aacbbdc887f424a7b03cba3ab593bf" -dependencies = [ - "backtrace", - "fnv", - "gl_generator", - "glutin", - "lazy_static", - "memoffset", - "smallvec", - "takeable-option", -] - [[package]] name = "glob" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "glow" @@ -1749,56 +1829,55 @@ dependencies = [ ] [[package]] -name = "glutin" -version = "0.29.1" +name = "glow" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "444c9ad294fdcaf20ccf6726b78f380b5450275540c9b68ab62f49726ad1c713" +checksum = "8edf6019dff2d92ad27c1e3ff82ad50a0aea5b01370353cc928bfdc33e95925c" dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin" +version = "0.30.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524d807cd49a0c56a53ef9a6738cd15e7c8c4e9d37a3b7fdb3c250c1cd5bf7a3" +dependencies = [ + "bitflags", + "cfg_aliases", "cgl", "cocoa", "core-foundation", "glutin_egl_sys", - "glutin_gles2_sys", "glutin_glx_sys", "glutin_wgl_sys", "libloading", - "log", "objc", "once_cell", - "osmesa-sys", - "parking_lot", - "raw-window-handle 0.5.0", - "wayland-client", - "wayland-egl", - "winapi", - "winit", + "raw-window-handle", + "wayland-sys 0.30.1", + "windows-sys 0.36.1", + "x11-dl", ] [[package]] name = "glutin_egl_sys" -version = "0.1.6" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68900f84b471f31ea1d1355567eb865a2cf446294f06cef8d653ed7bcf5f013d" +checksum = "3adbb8fec0e18e340f990c78f79f5f0e142d0d83f46b10909aaa7d251c00afdf" dependencies = [ "gl_generator", - "winapi", -] - -[[package]] -name = "glutin_gles2_sys" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094e708b730a7c8a1954f4f8a31880af00eb8a1c5b5bf85d28a0a3c6d69103" -dependencies = [ - "gl_generator", - "objc", + "windows-sys 0.36.1", ] [[package]] name = "glutin_glx_sys" -version = "0.1.8" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93d0575865098580c5b3a423188cd959419912ea60b1e48e8b3b526f6d02468" +checksum = "947c4850c58211c9627969c2b4e2674764b81ae5b47bab2c9a477d7942f96e0f" dependencies = [ "gl_generator", "x11-dl", @@ -1806,9 +1885,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.1.5" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3da5951a1569dbab865c6f2a863efafff193a93caf05538d193e9e3816d21696" +checksum = "20c33975a6c9d49d72c8f032a60079bf8df536954fbf9e4cee90396ace815c57" dependencies = [ "gl_generator", ] @@ -1843,6 +1922,19 @@ dependencies = [ "bitflags", ] +[[package]] +name = "gpu-allocator" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434618454f74b63f9b39328298097256977c41ea0ba9d75a47238b77790b6163" +dependencies = [ + "backtrace", + "log", + "thiserror", + "winapi", + "windows 0.43.0", +] + [[package]] name = "gpu-descriptor" version = "0.2.3" @@ -1851,7 +1943,7 @@ checksum = "0b0c02e1ba0bdb14e965058ca34e09c020f8e507a760df1121728e0aef68d57a" dependencies = [ "bitflags", "gpu-descriptor-types", - "hashbrown 0.12.3", + "hashbrown", ] [[package]] @@ -1891,15 +1983,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "hashbrown" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04" -dependencies = [ - "ahash 0.4.7", -] - [[package]] name = "hashbrown" version = "0.12.3" @@ -1910,10 +1993,25 @@ dependencies = [ ] [[package]] -name = "heck" -version = "0.4.0" +name = "hassle-rs" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9" +checksum = "90601c6189668c7345fc53842cb3f3a3d872203d523be1b3cb44a36a3e62fb85" +dependencies = [ + "bitflags", + "com-rs", + "libc", + "libloading", + "thiserror", + "widestring", + "winapi", +] + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" [[package]] name = "hello_world" @@ -1923,6 +2021,13 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "hello_world_par" +version = "0.1.0" +dependencies = [ + "eframe", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -1946,17 +2051,28 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "iana-time-zone" -version = "0.1.46" +version = "0.1.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad2bfd338099682614d3ee3fe0cd72e0b6a41ca6a87f6a74a3bd593c91650501" +checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" dependencies = [ "android_system_properties", "core-foundation-sys", + "iana-time-zone-haiku", "js-sys", "wasm-bindgen", "winapi", ] +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" +dependencies = [ + "cxx", + "cxx-build", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -1965,20 +2081,19 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.2.3" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" dependencies = [ - "matches", "unicode-bidi", "unicode-normalization", ] [[package]] name = "image" -version = "0.24.3" +version = "0.24.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e30ca2ecf7666107ff827a8e481de6a132a9b687ed3bb20bb1c144a36c00964" +checksum = "69b7ea949b537b0fd0af141fff8c77690f2ce96f4f41f042ccb6c69c6c965945" dependencies = [ "bytemuck", "byteorder", @@ -1990,13 +2105,19 @@ dependencies = [ ] [[package]] -name = "indexmap" -version = "1.9.1" +name = "imagesize" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e" +checksum = "df19da1e92fbfec043ca97d622955381b1f3ee72a180ec999912df31b1ccd951" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown 0.12.3", + "hashbrown", ] [[package]] @@ -2013,24 +2134,24 @@ dependencies = [ [[package]] name = "itertools" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itoa" -version = "1.0.3" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" +checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" [[package]] name = "jni" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec" +checksum = "039022cdf4d7b1cf548d31f60ae783138e5fd42013f6271049d7df7afadef96c" dependencies = [ "cesu8", "combine", @@ -2048,18 +2169,18 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.24" +version = "0.1.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa" +checksum = "068b1ee6743e4d11fb9c6a1e6064b3693a1b600e7f5f5988047d98b3dc9fb90b" dependencies = [ "libc", ] [[package]] name = "jpeg-decoder" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9478aa10f73e7528198d75109c8be5cd7d15fb530238040148d5f9a22d4c5b3b" +checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e" [[package]] name = "js-sys" @@ -2070,6 +2191,14 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "keyboard_events" +version = "0.1.0" +dependencies = [ + "eframe", + "tracing-subscriber", +] + [[package]] name = "khronos-egl" version = "4.1.0" @@ -2093,7 +2222,8 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a53776d271cfb873b17c618af0298445c88afc52837f3e948fa3fafd131f449" dependencies = [ - "arrayvec 0.7.2", + "arrayvec", + "serde", ] [[package]] @@ -2110,15 +2240,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.132" +version = "0.2.139" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5" +checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" [[package]] name = "libloading" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbc0f03f9a775e9f6aed295c6a1ba2253c5757a9e03d55c6caa46a681abcddd" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" dependencies = [ "cfg-if", "winapi", @@ -2126,9 +2256,9 @@ dependencies = [ [[package]] name = "libm" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565" +checksum = "348108ab3fba42ec82ff6e9564fc4ca0247bdccdc68dd8af9764bbc79c3c8ffb" [[package]] name = "line-wrap" @@ -2139,6 +2269,15 @@ dependencies = [ "safemem", ] +[[package]] +name = "link-cplusplus" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" +dependencies = [ + "cc", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2146,10 +2285,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" [[package]] -name = "lock_api" -version = "0.4.7" +name = "litrs" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53" +checksum = "f9275e0933cf8bb20f008924c0cb07a0692fe54d8064996520bf998de9eb79aa" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" dependencies = [ "autocfg", "scopeguard", @@ -2173,12 +2318,6 @@ dependencies = [ "libc", ] -[[package]] -name = "matches" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" - [[package]] name = "memchr" version = "2.5.0" @@ -2187,9 +2326,9 @@ checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "memmap2" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95af15f345b17af2efc8ead6080fb8bc376f8cec1b35277b935637595fe77498" +checksum = "4b182332558b18d807c4ce1ca8ca983b34c3ee32765e47b3f0f69b90355cc1dc" dependencies = [ "libc", ] @@ -2212,7 +2351,7 @@ dependencies = [ "bitflags", "block", "core-graphics-types", - "foreign-types 0.3.2", + "foreign-types", "log", "objc", ] @@ -2225,9 +2364,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.5.3" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f5c75688da582b8ffc1f1799e9db273f32133c49e048f614d22ec3256773ccc" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" dependencies = [ "adler", ] @@ -2240,21 +2379,21 @@ checksum = "e53debba6bda7a793e5f99b8dacf19e626084f525f7829104ba9898f367d85ff" [[package]] name = "mio" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57ee1c23c7c63b0c9250c339ffdc69255f110b298b901b9f6c82547b7b87caaf" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" dependencies = [ "libc", "log", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", + "windows-sys 0.42.0", ] [[package]] name = "naga" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "262d2840e72dbe250e8cf2f522d080988dfca624c4112c096238a4845f591707" +checksum = "5eafe22a23b797c9bc227c6c896419b26b5bb88fa903417a3adaed08778850d5" dependencies = [ "bit-set", "bitflags", @@ -2270,19 +2409,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "ndk" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" -dependencies = [ - "bitflags", - "jni-sys", - "ndk-sys 0.3.0", - "num_enum", - "thiserror", -] - [[package]] name = "ndk" version = "0.7.0" @@ -2291,9 +2417,9 @@ checksum = "451422b7e4718271c8b5b3aadf5adedba43dc76312454b387e98fae0fc951aa0" dependencies = [ "bitflags", "jni-sys", - "ndk-sys 0.4.0", + "ndk-sys", "num_enum", - "raw-window-handle 0.5.0", + "raw-window-handle", "thiserror", ] @@ -2303,21 +2429,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-glue" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d0c4a7b83860226e6b4183edac21851f05d5a51756e97a1144b7f5a6b63e65f" -dependencies = [ - "lazy_static", - "libc", - "log", - "ndk 0.6.0", - "ndk-context", - "ndk-macro", - "ndk-sys 0.3.0", -] - [[package]] name = "ndk-glue" version = "0.7.0" @@ -2326,10 +2437,10 @@ checksum = "0434fabdd2c15e0aab768ca31d5b7b333717f03cf02037d5a0a3ff3c278ed67f" dependencies = [ "libc", "log", - "ndk 0.7.0", + "ndk", "ndk-context", "ndk-macro", - "ndk-sys 0.4.0", + "ndk-sys", "once_cell", "parking_lot", ] @@ -2340,7 +2451,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0df7ac00c4672f9d5aece54ee3347520b7e20f158656c7db2e6de01902eb7a6c" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro-crate", "proc-macro2", "quote", @@ -2349,30 +2460,20 @@ dependencies = [ [[package]] name = "ndk-sys" -version = "0.3.0" +version = "0.4.1+23.1.7779620" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e5a6ae77c8ee183dcbbba6150e2e6b9f3f4196a7666c02a715a95692ec1fa97" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "ndk-sys" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d83ec9c63ec5bf950200a8e508bdad6659972187b625469f58ef8c08e29046" +checksum = "3cf2aae958bd232cac5069850591667ad422d263686d75b52a065f9badeee5a3" dependencies = [ "jni-sys", ] [[package]] name = "nix" -version = "0.22.3" +version = "0.24.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" +checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069" dependencies = [ "bitflags", - "cc", "cfg-if", "libc", "memoffset", @@ -2380,27 +2481,16 @@ dependencies = [ [[package]] name = "nix" -version = "0.23.1" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nix" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "195cdbc1741b8134346d515b3a56a1c94b0912758009cfd53f99ea0f57b065fc" +checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4" dependencies = [ + "autocfg", "bitflags", "cfg-if", "libc", "memoffset", + "pin-utils", ] [[package]] @@ -2411,14 +2501,33 @@ checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" [[package]] name = "nom" -version = "7.1.1" +version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] +[[package]] +name = "nom8" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae01545c9c7fc4486ab7debaf2aad7003ac19431791868fb2e8066df97fad2f8" +dependencies = [ + "memchr", +] + +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2452,18 +2561,18 @@ dependencies = [ [[package]] name = "num_enum" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf5395665662ef45796a4ff5486c5d41d29e0c09640af4c5f17fd94ee2c119c9" +checksum = "8d829733185c1ca374f17e52b762f24f535ec625d2cc1f070e34c8a9068f341b" dependencies = [ "num_enum_derive", ] [[package]] name = "num_enum_derive" -version = "0.5.7" +version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b0498641e53dd6ac1a4f22547548caa6864cc4933784319cd1775271c5a46ce" +checksum = "2be1598bf1c313dcdd12092e3f1920f463462525a21b7b4e11b4168353d0123e" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -2471,15 +2580,6 @@ dependencies = [ "syn", ] -[[package]] -name = "num_threads" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" -dependencies = [ - "libc", -] - [[package]] name = "objc" version = "0.2.7" @@ -2501,6 +2601,32 @@ dependencies = [ "objc_id", ] +[[package]] +name = "objc-sys" +version = "0.2.0-beta.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b9834c1e95694a05a828b59f55fa2afec6288359cda67146126b3f90a55d7" + +[[package]] +name = "objc2" +version = "0.3.0-beta.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe31e5425d3d0b89a15982c024392815da40689aceb34bad364d58732bcfd649" +dependencies = [ + "block2", + "objc-sys", + "objc2-encode", +] + +[[package]] +name = "objc2-encode" +version = "2.0.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abfcac41015b00a120608fdaa6938c44cb983fee294351cc4bac7638b4e50512" +dependencies = [ + "objc-sys", +] + [[package]] name = "objc_exception" version = "0.1.2" @@ -2521,18 +2647,18 @@ dependencies = [ [[package]] name = "object" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" +checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.13.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e" +checksum = "6f61fba1741ea2b3d6a1e3178721804bb716a68a6aeba1149b5d52e3d464ea66" [[package]] name = "oorandom" @@ -2541,20 +2667,32 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] -name = "ordered-multimap" -version = "0.3.1" +name = "orbclient" +version = "0.3.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c672c7ad9ec066e428c00eb917124a06f08db19e2584de982cc34b1f4c12485" +checksum = "ba683f1641c11041c59d5d93689187abcab3c1349dc6d9d70c550c9f9360802f" +dependencies = [ + "cfg-if", + "redox_syscall 0.2.16", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "ordered-multimap" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" dependencies = [ "dlv-list", - "hashbrown 0.9.1", + "hashbrown", ] [[package]] name = "ordered-stream" -version = "0.0.1" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44630c059eacfd6e08bdaa51b1db2ce33119caa4ddc1235e923109aa5f25ccb1" +checksum = "360a24bdacdb7801a1a6af8500392864791c130ebe8bd9a063158cab00040c90" dependencies = [ "futures-core", "pin-project-lite", @@ -2562,28 +2700,31 @@ dependencies = [ [[package]] name = "os_str_bytes" -version = "6.3.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ff7415e9ae3fff1225851df9e0d9e4e5479f947619774677a63572e55e80eff" +checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" [[package]] -name = "osmesa-sys" -version = "0.1.2" +name = "overload" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88cfece6e95d2e717e0872a7f53a8684712ad13822a7979bc760b9c77ec0013b" -dependencies = [ - "shared_library", -] +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "owned_ttf_parser" -version = "0.15.1" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ef1a404ae479dd6906f4fa2c88b3c94028f1284beb42a47c183a7c27ee9a3e" +checksum = "e25e9fb15717794fae58ab55c26e044103aad13186fbb625893f9a3bbcc24228" dependencies = [ "ttf-parser", ] +[[package]] +name = "oxilangtag" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d91edf4fbb970279443471345a4e8c491bf05bb283b3e6c88e4e606fd8c181b" + [[package]] name = "pango-sys" version = "0.15.10" @@ -2614,17 +2755,23 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.3" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a279cbf25cb0757810394fbc1e359949b59e348145c643a939a525692e6929" +checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", - "windows-sys", + "windows-sys 0.45.0", ] +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + [[package]] name = "peeking_take_while" version = "0.1.2" @@ -2633,9 +2780,9 @@ checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" [[package]] name = "percent-encoding" -version = "2.1.0" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" [[package]] name = "pico-args" @@ -2657,56 +2804,57 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "pkg-config" -version = "0.3.25" +version = "0.3.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" +checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" [[package]] name = "plist" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd39bc6cdc9355ad1dc5eeedefee696bb35c34caf21768741e81826c0bbd7225" +checksum = "5329b8f106a176ab0dce4aae5da86bfcb139bb74fb00882859e03745011f3635" dependencies = [ "base64", "indexmap", "line-wrap", + "quick-xml", "serde", - "time 0.3.13", - "xml-rs", + "time 0.3.17", ] [[package]] name = "png" -version = "0.17.5" +version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba" +checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" dependencies = [ "bitflags", "crc32fast", - "deflate", + "flate2", "miniz_oxide", ] [[package]] name = "poll-promise" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260817b339544e5b23d4bb66d4522d89dd64af88d16b6dcd7b2a2409ee2e095d" +checksum = "fcf2a02372dfae23c9c01267fb296b8a3413bb4e45fbd589c3ac73c6dcfbb305" dependencies = [ "static_assertions", ] [[package]] name = "polling" -version = "2.2.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685404d509889fade3e86fe3a5803bca2ec09b0c0778d5ada6ec8bf7a8de5259" +checksum = "22122d5ec4f9fe1b3916419b76be1e80bcb93f618d071d2edf841b137b2a2bd6" dependencies = [ + "autocfg", "cfg-if", "libc", "log", "wepoll-ffi", - "winapi", + "windows-sys 0.42.0", ] [[package]] @@ -2717,45 +2865,45 @@ checksum = "5da3b0203fd7ee5720aa0b5e790b591aa5d3f41c3ed2c34a3a393382198af2f7" [[package]] name = "ppv-lite86" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro-crate" -version = "1.2.1" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda0fc3b0fb7c975631757e14d9049da17374063edb6ebbcbc54d880d4fe94e9" +checksum = "66618389e4ec1c7afe67d51a9bf34ff9236480f8d51e7489b7d5ab0303c13f34" dependencies = [ "once_cell", - "thiserror", - "toml", + "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.43" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] [[package]] name = "profiling" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f61dcf0b917cd75d4521d7343d1ffff3d1583054133c9b5cbea3375c703c40d" +checksum = "74605f360ce573babfe43964cbe520294dcb081afbf8c108fc6e23036b4da2df" [[package]] name = "puffin" -version = "0.14.0" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796a1b7d7d0ec984dde24615178cbd14dc697ea4cdcddfd1fee9a5f87135f9e8" +checksum = "d6755c40ac18b4af2c6dc147fde017de518bdacbf63f5969490f3b62e7c33680" dependencies = [ "anyhow", "bincode", "byteorder", + "instant", "once_cell", "parking_lot", "ruzstd", @@ -2765,9 +2913,9 @@ dependencies = [ [[package]] name = "puffin_http" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77dae1a51a8887f161ae17809ec4159bb3fbc8be60d12947039fa063cf211f1b" +checksum = "dd0248f6b7425d45ac24cecefc9272d679b8fc18e82c5d3777e90146eb9a9f85" dependencies = [ "anyhow", "crossbeam-channel", @@ -2785,10 +2933,19 @@ dependencies = [ ] [[package]] -name = "quote" -version = "1.0.21" +name = "quick-xml" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" dependencies = [ "proc-macro2", ] @@ -2816,9 +2973,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ "getrandom", ] @@ -2829,15 +2986,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e935c45e09cc6dcf00d2f0b2d630a58f4095320223d47fc68918722f0538b6" -[[package]] -name = "raw-window-handle" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b800beb9b6e7d2df1fe337c9e3d04e3af22a124460fb4c30fcc22c9117cefb41" -dependencies = [ - "cty", -] - [[package]] name = "raw-window-handle" version = "0.5.0" @@ -2849,9 +2997,9 @@ dependencies = [ [[package]] name = "rctree" -version = "0.4.0" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae028b272a6e99d9f8260ceefa3caa09300a8d6c8d2b2001316474bc52122e9" +checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f" [[package]] name = "redox_syscall" @@ -2862,6 +3010,15 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_syscall" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb02a9aee8e8c7ad8d86890f1e16b49e0bbbffc9961ff3788c31d57c98bcbf03" +dependencies = [ + "bitflags", +] + [[package]] name = "redox_users" version = "0.4.3" @@ -2869,15 +3026,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] [[package]] name = "regex" -version = "1.6.0" +version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4eb3267174b8c6c2f654116623910a0fef09c4753f8dd83db29c48a0df988b" +checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ "aho-corasick", "memchr", @@ -2886,9 +3043,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.6.27" +version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" [[package]] name = "remove_dir_all" @@ -2907,19 +3064,15 @@ checksum = "f1382d1f0a252c4bf97dc20d979a2fdd05b024acd7c2ed0f7595d7817666a157" [[package]] name = "resvg" -version = "0.23.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34489194784b86c03c3d688258e2ba73f3c82700ba4673ee2ecad5ae540b9438" +checksum = "c115863f2d3621999cf187e318bc92b16402dfeff6a48c74df700d77381394c1" dependencies = [ - "gif", - "jpeg-decoder", "log", "pico-args", - "png", "rgb", - "svgfilters", "svgtypes", - "tiny-skia 0.6.6", + "tiny-skia", "usvg", ] @@ -2949,7 +3102,7 @@ dependencies = [ "objc", "objc-foundation", "objc_id", - "raw-window-handle 0.5.0", + "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -2958,9 +3111,9 @@ dependencies = [ [[package]] name = "rgb" -version = "0.8.33" +version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3b221de559e4a29df3b957eec92bc0de6bc8eaf6ca9cfed43e5e1d67ff65a34" +checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" dependencies = [ "bytemuck", ] @@ -2993,18 +3146,18 @@ dependencies = [ [[package]] name = "roxmltree" -version = "0.14.1" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "921904a62e410e37e215c40381b7117f830d9d89ba60ab5236170541dd25646b" +checksum = "6b9de9831a129b122e7e61f242db509fa9d0838008bf0b29bb0624669edfe48a" dependencies = [ "xmlparser", ] [[package]] name = "rust-ini" -version = "0.17.0" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63471c4aa97a1cf8332a5f97709a79a4234698de6a1f5087faf66f2dae810e22" +checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" dependencies = [ "cfg-if", "ordered-multimap", @@ -3024,9 +3177,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustls" -version = "0.20.6" +version = "0.20.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aab8ee6c7097ed6057f43c187a62418d0c05a4bd5f18b3571db50ee0f9ce033" +checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" dependencies = [ "log", "ring", @@ -3034,22 +3187,6 @@ dependencies = [ "webpki", ] -[[package]] -name = "rustybuzz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a617c811f5c9a7060fe511d35d13bf5b9f0463ce36d63ce666d05779df2b4eba" -dependencies = [ - "bitflags", - "bytemuck", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-general-category", - "unicode-script", -] - [[package]] name = "ruzstd" version = "0.3.0" @@ -3063,18 +3200,9 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.11" +version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" - -[[package]] -name = "safe_arch" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05" -dependencies = [ - "bytemuck", -] +checksum = "7b4b9743ed687d4b4bcedf9ff5eaa7398495ae14e61cba0a295704edbc7decde" [[package]] name = "safemem" @@ -3093,9 +3221,9 @@ dependencies = [ [[package]] name = "scoped-tls" -version = "1.0.0" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" @@ -3103,12 +3231,17 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scratch" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddccb15bcce173023b3fedd9436f882a0739b8dfb45e4f6b6002bee5929f61b2" + [[package]] name = "screenshot" version = "0.1.0" dependencies = [ "eframe", - "egui_extras", "itertools", ] @@ -3124,30 +3257,43 @@ dependencies = [ [[package]] name = "sctk-adwaita" -version = "0.4.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04b7c47a572f73de28bee5b5060d085b42b6ce1e4ee2b49c956ea7b25e94b6f0" +checksum = "cc56402866c717f54e48b122eb93c69f709bc5a6359c403598992fd92f017931" dependencies = [ - "crossfont", + "ab_glyph", "log", + "memmap2", "smithay-client-toolkit", - "tiny-skia 0.7.0", + "tiny-skia", ] [[package]] name = "serde" -version = "1.0.143" +version = "1.0.152" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e8e5d5b70924f74ff5c6d64d9a5acd91422117c60f48c4e07855238a254553" +checksum = "bb7d1f0d3021d347a83e556fc4683dea2ea09d87bccdf88ff5c12545d89d5efb" dependencies = [ "serde_derive", ] [[package]] -name = "serde_derive" -version = "1.0.143" +name = "serde-xml-rs" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3d8e8de557aee63c26b85b947f5e59b690d0454c753f3adeb5cd7835ab88391" +checksum = "f0bf1ba0696ccf0872866277143ff1fd14d22eec235d2b23702f95e6660f7dfa" +dependencies = [ + "log", + "serde", + "thiserror", + "xml-rs", +] + +[[package]] +name = "serde_derive" +version = "1.0.152" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af487d118eecd09402d70a5d72551860e788df87b464af30e5ea6a38c75c541e" dependencies = [ "proc-macro2", "quote", @@ -3156,9 +3302,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.83" +version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +checksum = "877c235533714907a8c2464236f5c4b2a17262ef1bd71f38f35ea592c8da6883" dependencies = [ "itoa", "ryu", @@ -3167,9 +3313,9 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fe39d9fbb0ebf5eb2c7cb7e2a47e4f462fad1379f1166b8ae49ad9eae89a7ca" +checksum = "9a5ec9fa74a20ebbe5d9ac23dac1fc96ba0ecfe9f50f2843b52e537b10fbcb4e" dependencies = [ "proc-macro2", "quote", @@ -3183,42 +3329,17 @@ dependencies = [ "eframe", ] -[[package]] -name = "servo-fontconfig" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7e3e22fe5fd73d04ebf0daa049d3efe3eae55369ce38ab16d07ddd9ac5c217c" -dependencies = [ - "libc", - "servo-fontconfig-sys", -] - -[[package]] -name = "servo-fontconfig-sys" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e36b879db9892dfa40f95da1c38a835d41634b825fbd8c4c418093d53c24b388" -dependencies = [ - "expat-sys", - "freetype-sys", - "pkg-config", -] - [[package]] name = "sha1" -version = "0.6.1" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" dependencies = [ - "sha1_smol", + "cfg-if", + "cpufeatures", + "digest", ] -[[package]] -name = "sha1_smol" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" - [[package]] name = "sharded-slab" version = "0.1.4" @@ -3228,16 +3349,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "shared_library" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9e7e0f2bfae24d8a5b5a66c5b257a83c7412304311512a0c054cd5e619da11" -dependencies = [ - "lazy_static", - "libc", -] - [[package]] name = "shlex" version = "1.1.0" @@ -3279,9 +3390,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fd0db749597d91ff862fd1d55ea87f7855a744a8425a64695b6fca237d1dad1" +checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" [[package]] name = "smithay-client-toolkit" @@ -3295,7 +3406,7 @@ dependencies = [ "lazy_static", "log", "memmap2", - "nix 0.24.2", + "nix 0.24.3", "pkg-config", "wayland-client", "wayland-cursor", @@ -3314,9 +3425,9 @@ dependencies = [ [[package]] name = "socket2" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66d72b759436ae32898a2af0a14218dbf55efde3feeb170eb623637db85ee1e0" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" dependencies = [ "libc", "winapi", @@ -3324,9 +3435,9 @@ dependencies = [ [[package]] name = "speech-dispatcher" -version = "0.15.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d62720b035474bccfd208cb85b1772adfae4b3450c743853e2e7b9c67e441e" +checksum = "5727d53c474ba5ada07784ad7d203cf896a74854cfee0eb32376b00759eb2972" dependencies = [ "lazy_static", "libc", @@ -3370,6 +3481,15 @@ version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e08d8363704e6c71fc928674353e6b7c23dcea9d82d7012c8faf2a3a025f8d0" +[[package]] +name = "strict-num" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df65f20698aeed245efdde3628a6b559ea1239bbb871af1b6e3b58c413b2bd1" +dependencies = [ + "float-cmp", +] + [[package]] name = "strsim" version = "0.10.0" @@ -3384,48 +3504,26 @@ dependencies = [ "egui_extras", ] -[[package]] -name = "svgfilters" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "639abcebc15fdc2df179f37d6f5463d660c1c79cd552c12343a4600827a04bce" -dependencies = [ - "float-cmp", - "rgb", -] - [[package]] name = "svgtypes" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc802f68b144cdf4d8ff21301f9a7863e837c627fde46537e29c05e8a18c85c1" +checksum = "22975e8a2bac6a76bb54f898a6b18764633b00e780330f0b689f65afb3975564" dependencies = [ "siphasher", ] [[package]] name = "syn" -version = "1.0.99" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13" +checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] -[[package]] -name = "synstructure" -version = "0.12.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" -dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", -] - [[package]] name = "syntect" version = "5.0.0" @@ -3451,9 +3549,9 @@ dependencies = [ [[package]] name = "system-deps" -version = "6.0.2" +version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a45a1c4c9015217e12347f2a411b57ce2c4fc543913b14b6fe40483328e709" +checksum = "2955b1fe31e1fa2fbd1976b71cc69a606d7d4da16f6de3333d0c92d51419aeff" dependencies = [ "cfg-expr", "heck", @@ -3462,12 +3560,6 @@ dependencies = [ "version-compare", ] -[[package]] -name = "takeable-option" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ae8932fcfea38b7d3883ae2ab357b0d57a02caaa18ebb4f5ece08beaec4aa0" - [[package]] name = "tempfile" version = "3.3.0" @@ -3477,40 +3569,40 @@ dependencies = [ "cfg-if", "fastrand", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "remove_dir_all", "winapi", ] [[package]] name = "termcolor" -version = "1.1.3" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bab24d30b911b2376f3a13cc2cd443142f0c81dda04c118693e35b3835757755" +checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" dependencies = [ "winapi-util", ] [[package]] name = "textwrap" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +checksum = "222a222a5bfe1bba4a77b45ec488a741b3cb8872e5e499451fd7d0129c9c7c3d" [[package]] name = "thiserror" -version = "1.0.32" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +checksum = "6a9cd18aa97d5c45c6603caea1da6628790b37f7a34b6ca89522331c5180fed0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.32" +version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +checksum = "1fb327af4685e4d03fa8cbcf1716380da910eeb2bb8be417e7f9fd3fb164f36f" dependencies = [ "proc-macro2", "quote", @@ -3533,7 +3625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f7a5a1017829335f6761bdbae2daf3021a88314a1f8d5f188c9b62ce62e2fd31" dependencies = [ "cgmath", - "glow", + "glow 0.11.2", "instant", "thiserror", "three-d-asset", @@ -3555,9 +3647,9 @@ dependencies = [ [[package]] name = "time" -version = "0.1.44" +version = "0.1.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" dependencies = [ "libc", "wasi 0.10.0+wasi-snapshot-preview1", @@ -3566,61 +3658,54 @@ dependencies = [ [[package]] name = "time" -version = "0.3.13" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db76ff9fa4b1458b3c7f077f3ff9887394058460d21e634355b273aaf11eea45" +checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ "itoa", - "libc", - "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" + +[[package]] +name = "time-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d967f99f534ca7e495c575c62638eebc2898a8c84c119b89e250477bc4ba16b2" +dependencies = [ + "time-core", ] [[package]] name = "tiny-skia" -version = "0.6.6" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d049bfef0eaa2521e75d9ffb5ce86ad54480932ae19b85f78bec6f52c4d30d78" +checksum = "0ae12c22601b6853f4d93abb178e13bf0e1cc8e2454100c85d4d3a59ac71b3f7" dependencies = [ "arrayref", - "arrayvec 0.5.2", + "arrayvec", "bytemuck", "cfg-if", "png", - "safe_arch", -] - -[[package]] -name = "tiny-skia" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "642680569bb895b16e4b9d181c60be1ed136fa0c9c7f11d004daf053ba89bf82" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "bytemuck", - "cfg-if", - "png", - "safe_arch", "tiny-skia-path", ] [[package]] name = "tiny-skia-path" -version = "0.7.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c114d32f0c2ee43d585367cb013dfaba967ab9f62b90d9af0d696e955e70fa6c" +checksum = "bd665853ce64402daabef6edda442dbb4f8ee93ea80957b66ba1af419f11a104" dependencies = [ "arrayref", "bytemuck", -] - -[[package]] -name = "tinystr" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8aeafdfd935e4a7fe16a91ab711fa52d54df84f9c8f7ca5837a9d1d902ef4c2" -dependencies = [ - "displaydoc", + "strict-num", ] [[package]] @@ -3644,24 +3729,41 @@ dependencies = [ [[package]] name = "tinyvec_macros" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "toml" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d82e1a7758622a465f8cee077614c73484dac5b836c02ff6a40d5d1010324d7" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" dependencies = [ "serde", ] [[package]] -name = "tracing" -version = "0.1.36" +name = "toml_datetime" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fce9567bd60a67d08a16488756721ba392f24f29006402881e43b19aac64307" +checksum = "4553f467ac8e3d374bc9a177a26801e5d0f9b211aa1673fb137a403afd1c9cf5" + +[[package]] +name = "toml_edit" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c59d8dd7d0dcbc6428bf7aa2f0e823e26e43b3c9aca15bbc9475d23e5fa12b" +dependencies = [ + "indexmap", + "nom8", + "toml_datetime", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" dependencies = [ "cfg-if", "pin-project-lite", @@ -3671,9 +3773,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.22" +version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11c75893af559bc8e10716548bdef5cb2b983f8e637db9d0e15126b61b484ee2" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" dependencies = [ "proc-macro2", "quote", @@ -3682,9 +3784,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aeea4303076558a00714b823f9ad67d58a3bbda1df83d8827d21193156e22f7" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" dependencies = [ "once_cell", "valuable", @@ -3703,11 +3805,11 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.15" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60db860322da191b40952ad9affe65ea23e7dd6a5c442c2c42865810c6ab8e6b" +checksum = "a6176eae26dd70d0c919749377897b54a9276bd7061339665dd68777926b5a70" dependencies = [ - "ansi_term", + "nu-ansi-term", "sharded-slab", "smallvec", "thread_local", @@ -3728,15 +3830,15 @@ dependencies = [ [[package]] name = "ttf-parser" -version = "0.15.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b3e06c9b9d80ed6b745c7159c40b311ad2916abb34a49e9be2653b90db0d8dd" +checksum = "0609f771ad9c6155384897e1df4d948e692667cc0588548b68eb44d052b27633" [[package]] name = "tts" -version = "0.24.3" +version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "810084d246c730f3cadc70fb74ceb4d1abf84d8bcdb7dc81e55ac808992da2c8" +checksum = "b7d2f3540357cbcd1d3f16aefc6357ba2fb3960591d1468eb951c9021f854338" dependencies = [ "cocoa-foundation", "core-foundation", @@ -3745,14 +3847,15 @@ dependencies = [ "lazy_static", "libc", "log", - "ndk-glue 0.6.2", + "ndk-context", + "ndk-glue", "objc", + "oxilangtag", "speech-dispatcher", "thiserror", - "unic-langid", "wasm-bindgen", "web-sys", - "windows 0.42.0", + "windows 0.43.0", ] [[package]] @@ -3774,6 +3877,12 @@ dependencies = [ "rustc-hash", ] +[[package]] +name = "typenum" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" + [[package]] name = "uds_windows" version = "1.0.2" @@ -3784,92 +3893,44 @@ dependencies = [ "winapi", ] -[[package]] -name = "unic-langid" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "398f9ad7239db44fd0f80fe068d12ff22d78354080332a5077dc6f52f14dcf2f" -dependencies = [ - "unic-langid-impl", -] - -[[package]] -name = "unic-langid-impl" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e35bfd2f2b8796545b55d7d3fd3e89a0613f68a0d1c8bc28cb7ff96b411a35ff" -dependencies = [ - "tinystr", -] - [[package]] name = "unicode-bidi" -version = "0.3.8" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992" - -[[package]] -name = "unicode-bidi-mirroring" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694" - -[[package]] -name = "unicode-ccc" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2520efa644f8268dce4dcd3050eaa7fc044fca03961e9998ac7e2e92b77cf1" - -[[package]] -name = "unicode-general-category" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07547e3ee45e28326cc23faac56d44f58f16ab23e413db526debce3b0bfd2742" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" [[package]] name = "unicode-ident" -version = "1.0.3" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf" +checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc" [[package]] name = "unicode-normalization" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854cbdc4f7bc6ae19c820d44abdc3277ac3e1b2b93db20a636825d9322fb60e6" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" dependencies = [ "tinyvec", ] -[[package]] -name = "unicode-script" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58dd944fd05f2f0b5c674917aea8a4df6af84f2d8de3fe8d988b95d28fb8fb09" - -[[package]] -name = "unicode-vo" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94" - [[package]] name = "unicode-width" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" +checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" [[package]] name = "unicode-xid" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e51f3646910546462e67d5f7599b9e4fb8acdd304b087a6494730f9eebf04" +checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" [[package]] name = "unicode_names2" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "029df4cc8238cefc911704ff8fa210853a0f3bce2694d8f51181dd41ee0f3301" +checksum = "446c96c6dd42604779487f0a981060717156648c1706aa1f464677f03c6cc059" [[package]] name = "untrusted" @@ -3879,12 +3940,11 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "ureq" -version = "2.5.0" +version = "2.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97acb4c28a254fd7a4aeec976c46a7fa404eac4d7c134b30c75144846d7cb8f" +checksum = "338b31dd1314f68f3aabf3ed57ab922df95ffcd902476ca7ba3c4ce7b908c46d" dependencies = [ "base64", - "chunked_transfer", "flate2", "log", "once_cell", @@ -3896,41 +3956,33 @@ dependencies = [ [[package]] name = "url" -version = "2.2.2" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", "idna", - "matches", "percent-encoding", ] [[package]] name = "usvg" -version = "0.23.0" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28a82565b5c96dcbb58c9bdbb6aa3642abd395a6a6b480658532c6f74c3c4b7a" +checksum = "8b5b7c2b30845b3348c067ca3d09e20cc6e327c288f0ca4c48698712abf432e9" dependencies = [ "base64", "data-url", "flate2", - "float-cmp", - "fontdb", + "imagesize", "kurbo", "log", - "pico-args", "rctree", "roxmltree", - "rustybuzz", "simplecss", "siphasher", + "strict-num", "svgtypes", - "ttf-parser", - "unicode-bidi", - "unicode-script", - "unicode-vo", - "xmlwriter", ] [[package]] @@ -3947,9 +3999,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version-compare" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" +checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29" [[package]] name = "version_check" @@ -4054,58 +4106,48 @@ checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" [[package]] name = "wayland-client" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91223460e73257f697d9e23d401279123d36039a3f7a449e983f123292d4458f" +checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715" dependencies = [ "bitflags", "downcast-rs", "libc", - "nix 0.22.3", + "nix 0.24.3", "scoped-tls", "wayland-commons", "wayland-scanner", - "wayland-sys", + "wayland-sys 0.29.5", ] [[package]] name = "wayland-commons" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f6e5e340d7c13490eca867898c4cec5af56c27a5ffe5c80c6fc4708e22d33e" +checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902" dependencies = [ - "nix 0.22.3", + "nix 0.24.3", "once_cell", "smallvec", - "wayland-sys", + "wayland-sys 0.29.5", ] [[package]] name = "wayland-cursor" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52758f13d5e7861fc83d942d3d99bf270c83269575e52ac29e5b73cb956a6bd" +checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661" dependencies = [ - "nix 0.22.3", + "nix 0.24.3", "wayland-client", "xcursor", ] -[[package]] -name = "wayland-egl" -version = "0.29.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83281d69ee162b59031c666385e93bde4039ec553b90c4191cdb128ceea29a3a" -dependencies = [ - "wayland-client", - "wayland-sys", -] - [[package]] name = "wayland-protocols" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60147ae23303402e41fe034f74fb2c35ad0780ee88a1c40ac09a3be1e7465741" +checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6" dependencies = [ "bitflags", "wayland-client", @@ -4115,9 +4157,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a1ed3143f7a143187156a2ab52742e89dac33245ba505c17224df48939f9e0" +checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53" dependencies = [ "proc-macro2", "quote", @@ -4126,15 +4168,27 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.29.4" +version = "0.29.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9341df79a8975679188e37dab3889bfa57c44ac2cb6da166f519a81cbe452d4" +checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4" dependencies = [ "dlib", "lazy_static", "pkg-config", ] +[[package]] +name = "wayland-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b2a02ac608e07132978689a6f9bf4214949c85998c247abadd4f4129b1aa06" +dependencies = [ + "dlib", + "lazy_static", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.60" @@ -4147,18 +4201,19 @@ dependencies = [ [[package]] name = "webbrowser" -version = "0.8.0" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01d62aa75495ab67cdc273d0b95cc76bcedfea2ba28338a4cf9b4137949dfac5" +checksum = "97d1fa1e5c829b2bf9eb1e28fb950248b797cd6a04866fbdfa8bc31e5eef4c78" dependencies = [ + "core-foundation", + "dirs", "jni", - "ndk-glue 0.7.0", + "log", + "ndk-context", "objc", - "raw-window-handle 0.5.0", + "raw-window-handle", "url", "web-sys", - "widestring", - "winapi", ] [[package]] @@ -4173,19 +4228,13 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.22.4" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1c760f0d366a6c24a02ed7816e23e691f5d92291f94d15e836006fd11b04daf" +checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" dependencies = [ "webpki", ] -[[package]] -name = "weezl" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb" - [[package]] name = "wepoll-ffi" version = "0.1.2" @@ -4197,16 +4246,18 @@ dependencies = [ [[package]] name = "wgpu" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2272b17bffc8a0c7d53897435da7c1db587c87d3a14e8dae9cdb8d1d210fc0f" +checksum = "d14c6bfcf3b10f4273f522a95994553c0a5f2934976e62e61a720ae4bc2eb8f2" dependencies = [ - "arrayvec 0.7.2", + "arrayvec", + "cfg-if", "js-sys", "log", "naga", "parking_lot", - "raw-window-handle 0.5.0", + "profiling", + "raw-window-handle", "smallvec", "static_assertions", "wasm-bindgen", @@ -4219,21 +4270,20 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73d14cad393054caf992ee02b7da6a372245d39a484f7461c1f44f6f6359bd28" +checksum = "be1f61be28e557a6ecb2506cac06c63fae3b6d302a006f38195a7a80995abeb9" dependencies = [ - "arrayvec 0.7.2", + "arrayvec", "bit-vec", "bitflags", - "cfg_aliases", "codespan-reporting", "fxhash", "log", "naga", "parking_lot", "profiling", - "raw-window-handle 0.5.0", + "raw-window-handle", "smallvec", "thiserror", "web-sys", @@ -4243,25 +4293,28 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "0.14.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdae6a80dbc725343f02f854b310b37190be946aeea65e9d83afaa7d840ebaac" +checksum = "82e95792925fe3d58950b9a5c2a191caa145e2bc570e2d233f0d7320f6a8e814" dependencies = [ "android_system_properties", - "arrayvec 0.7.2", + "arrayvec", "ash", "bit-set", "bitflags", "block", "core-graphics-types", "d3d12", - "foreign-types 0.3.2", + "foreign-types", "fxhash", - "glow", + "glow 0.12.0", "gpu-alloc", + "gpu-allocator", "gpu-descriptor", + "hassle-rs", "js-sys", "khronos-egl", + "libc", "libloading", "log", "metal", @@ -4270,7 +4323,7 @@ dependencies = [ "parking_lot", "profiling", "range-alloc", - "raw-window-handle 0.5.0", + "raw-window-handle", "renderdoc-sys", "smallvec", "thiserror", @@ -4282,22 +4335,24 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28fb86c1909233c804aa79b7dd1ad06ebd979b2a465e3e980582db0ea9e69f3f" +checksum = "ecf8cfcbf98f94cc8bd5981544c687140cf9d3948e2ab83849367ead2cd737cf" dependencies = [ "bitflags", + "js-sys", + "web-sys", ] [[package]] name = "which" -version = "4.2.5" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269" dependencies = [ "either", - "lazy_static", "libc", + "once_cell", ] [[package]] @@ -4365,13 +4420,40 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0286ba339aa753e70765d521bb0242cc48e1194562bfa2a2ad7ac8a6de28f5d5" dependencies = [ + "windows-implement", "windows_aarch64_gnullvm", - "windows_aarch64_msvc 0.42.0", - "windows_i686_gnu 0.42.0", - "windows_i686_msvc 0.42.0", - "windows_x86_64_gnu 0.42.0", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", "windows_x86_64_gnullvm", - "windows_x86_64_msvc 0.42.0", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04662ed0e3e5630dfa9b26e4cb823b817f1a9addda855d973a9458c236556244" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-implement" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9539b6bd3eadbd9de66c9666b22d802b833da7e996bc06896142e09854a61767" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -4388,10 +4470,49 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" +name = "windows-sys" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d2aa71f6f0cbe00ae5167d90ef3cfe66527d6f613ca78ac8024c3ccab9a19e" +checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc 0.42.1", + "windows_i686_gnu 0.42.1", + "windows_i686_msvc 0.42.1", + "windows_x86_64_gnu 0.42.1", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc 0.42.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" [[package]] name = "windows_aarch64_msvc" @@ -4407,9 +4528,9 @@ checksum = "2623277cb2d1c216ba3b578c0f3cf9cdebeddb6e66b1b218bb33596ea7769c3a" [[package]] name = "windows_aarch64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0f252f5a35cac83d6311b2e795981f5ee6e67eb1f9a7f64eb4500fbc4dcdb4" +checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" [[package]] name = "windows_i686_gnu" @@ -4425,9 +4546,9 @@ checksum = "d3925fd0b0b804730d44d4b6278c50f9699703ec49bcd628020f46f4ba07d9e1" [[package]] name = "windows_i686_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbeae19f6716841636c28d695375df17562ca208b2b7d0dc47635a50ae6c5de7" +checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" [[package]] name = "windows_i686_msvc" @@ -4443,9 +4564,9 @@ checksum = "ce907ac74fe331b524c1298683efbf598bb031bc84d5e274db2083696d07c57c" [[package]] name = "windows_i686_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c12f65daa39dd2babe6e442988fc329d6243fdce47d7d2d155b8d874862246" +checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" [[package]] name = "windows_x86_64_gnu" @@ -4461,15 +4582,15 @@ checksum = "2babfba0828f2e6b32457d5341427dcbb577ceef556273229959ac23a10af33d" [[package]] name = "windows_x86_64_gnu" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf7b1b21b5362cbc318f686150e5bcea75ecedc74dd157d874d754a2ca44b0ed" +checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" [[package]] name = "windows_x86_64_gnullvm" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09d525d2ba30eeb3297665bd434a54297e4170c7f1a44cad4ef58095b4cd2028" +checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" [[package]] name = "windows_x86_64_msvc" @@ -4485,18 +4606,19 @@ checksum = "f4dd6dc7df2d84cf7b33822ed5b86318fb1781948e9663bacd047fc9dd52259d" [[package]] name = "windows_x86_64_msvc" -version = "0.42.0" +version = "0.42.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40009d85759725a34da6d89a94e63d7bdc50a862acf0dbc7c8e488f1edcb6f5" +checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" [[package]] name = "winit" -version = "0.27.2" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a8f3e9d742401efcfe833b8f84960397482ff049cb7bf59a112e14a4be97f7" +checksum = "0c4755d4ba0e3d30fc7beef2095e246b1e6a6fad0717608bcb87a2df4b003bcf" dependencies = [ + "android-activity", "bitflags", - "cocoa", + "cfg_aliases", "core-foundation", "core-graphics", "dispatch", @@ -4504,50 +4626,42 @@ dependencies = [ "libc", "log", "mio", - "ndk 0.7.0", - "ndk-glue 0.7.0", - "objc", + "ndk", + "objc2", "once_cell", - "parking_lot", + "orbclient", "percent-encoding", - "raw-window-handle 0.4.3", - "raw-window-handle 0.5.0", + "raw-window-handle", + "redox_syscall 0.3.4", "sctk-adwaita", "smithay-client-toolkit", "wasm-bindgen", "wayland-client", + "wayland-commons", "wayland-protocols", + "wayland-scanner", "web-sys", - "windows-sys", + "windows-sys 0.45.0", "x11-dl", ] [[package]] name = "winreg" -version = "0.8.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d107f8c6e916235c4c01cabb3e8acf7bea8ef6a63ca2e7fa0527c049badfc48c" -dependencies = [ - "winapi", -] - -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" +checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" dependencies = [ "winapi", ] [[package]] name = "x11-dl" -version = "2.20.0" +version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c83627bc137605acc00bb399c7b908ef460b621fc37c953db2b09f88c449ea6" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" dependencies = [ - "lazy_static", "libc", + "once_cell", "pkg-config", ] @@ -4558,7 +4672,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "592b4883219f345e712b3209c62654ebda0bb50887f330cbd018d0f654bfd507" dependencies = [ "gethostname", - "nix 0.24.2", + "nix 0.24.3", "winapi", "winapi-wsapoll", "x11rb-protocol", @@ -4570,7 +4684,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56b245751c0ac9db0e006dc812031482784e434630205a93c73cfefcaabeac67" dependencies = [ - "nix 0.24.2", + "nix 0.24.3", ] [[package]] @@ -4590,15 +4704,9 @@ checksum = "d2d7d3948613f75c98fd9328cfdcc45acc4d360655289d0a7d4ec931392200a3" [[package]] name = "xmlparser" -version = "0.13.3" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8" - -[[package]] -name = "xmlwriter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9" +checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd" [[package]] name = "yaml-rust" @@ -4611,12 +4719,11 @@ dependencies = [ [[package]] name = "zbus" -version = "2.3.2" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d8f1a037b2c4a67d9654dc7bdfa8ff2e80555bbefdd3c1833c1d1b27c963a6b" +checksum = "23eaeb1859a3cd5c5f780b101dfe626bf250a5f34873c3c0226d6d9f7a4d107c" dependencies = [ "async-broadcast", - "async-channel", "async-executor", "async-io", "async-lock", @@ -4632,12 +4739,12 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "lazy_static", - "nix 0.23.1", + "nix 0.25.1", "once_cell", "ordered-stream", "rand", "serde", + "serde-xml-rs", "serde_repr", "sha1", "static_assertions", @@ -4651,9 +4758,9 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "2.3.2" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f8fb5186d1c87ae88cf234974c240671238b4a679158ad3b94ec465237349a6" +checksum = "e83c1c6d669caa4773ebe8cb2ebea2a8d0c6ea27fc6c5c8916c7308cbf1db3b1" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -4664,9 +4771,9 @@ dependencies = [ [[package]] name = "zbus_names" -version = "2.2.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a408fd8a352695690f53906dc7fd036be924ec51ea5e05666ff42685ed0af5" +checksum = "f34f314916bd89bdb9934154627fab152f4f28acdda03e7c4c68181b214fe7e3" dependencies = [ "serde", "static_assertions", @@ -4685,13 +4792,13 @@ dependencies = [ [[package]] name = "zerocopy-derive" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0fbc82b82efe24da867ee52e015e58178684bd9dd64c34e66bdf21da2582a9f" +checksum = "6505e6815af7de1746a08f69c69606bb45695a17149517680f3b2149713b19a3" dependencies = [ "proc-macro2", + "quote", "syn", - "synstructure", ] [[package]] @@ -4715,19 +4822,20 @@ dependencies = [ [[package]] name = "zstd-sys" -version = "2.0.1+zstd.1.5.2" +version = "2.0.6+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fd07cbbc53846d9145dbffdf6dd09a7a0aa52be46741825f5c97bdd4f73f12b" +checksum = "68a3f9792c0c3dc6c165840a75f47ae1f4da402c2d006881129579f6597e801b" dependencies = [ "cc", "libc", + "pkg-config", ] [[package]] name = "zvariant" -version = "3.6.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd68e4e6432ef19df47d7e90e2e72b5e7e3d778e0ae3baddf12b951265cc758" +checksum = "576cc41e65c7f283e5460f5818073e68fb1f1631502b969ef228c2e03c862efb" dependencies = [ "byteorder", "enumflags2", @@ -4739,9 +4847,9 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.6.0" +version = "3.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e977eaa3af652f63d479ce50d924254ad76722a6289ec1a1eac3231ca30430" +checksum = "0fd4aafc0dee96ae7242a24249ce9babf21e1562822f03df650d4e68c20e41ed" dependencies = [ "proc-macro-crate", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index eed3b6fc..89bf485f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,10 +1,10 @@ [workspace] resolver = "2" members = [ + "crates/ecolor", "crates/egui_demo_app", "crates/egui_demo_lib", "crates/egui_extras", - "crates/egui_glium", "crates/egui_glow", "crates/egui-wgpu", "crates/egui-winit", @@ -26,7 +26,8 @@ opt-level = 2 # fast and small wasm, basically same as `opt-level = 's'` [profile.dev] -split-debuginfo = "unpacked" # faster debug builds on mac +# Can't leave this on by default, because it breaks the Windows build. Related: https://github.com/rust-lang/cargo/issues/4897 +# split-debuginfo = "unpacked" # faster debug builds on mac # opt-level = 1 # Make debug builds run faster # Optimize all dependencies even in debug builds (does not affect workspace packages): diff --git a/Cranky.toml b/Cranky.toml index ce6c9e57..6fa6ca09 100644 --- a/Cranky.toml +++ b/Cranky.toml @@ -1,115 +1,124 @@ # https://github.com/ericseppanen/cargo-cranky # cargo install cargo-cranky && cargo cranky -deny = [ - "unsafe_code", -] +deny = ["unsafe_code"] warn = [ - "clippy::all", - "clippy::await_holding_lock", - "clippy::char_lit_as_u8", - "clippy::checked_conversions", - "clippy::dbg_macro", - "clippy::debug_assert_with_mut_call", - "clippy::disallowed_methods", - "clippy::disallowed_script_idents", - "clippy::doc_markdown", - "clippy::empty_enum", - "clippy::enum_glob_use", - "clippy::equatable_if_let", - "clippy::exit", - "clippy::expl_impl_clone_on_copy", - "clippy::explicit_deref_methods", - "clippy::explicit_into_iter_loop", - "clippy::explicit_iter_loop", - "clippy::fallible_impl_from", - "clippy::filter_map_next", - "clippy::flat_map_option", - "clippy::float_cmp_const", - "clippy::fn_params_excessive_bools", - "clippy::fn_to_numeric_cast_any", - "clippy::from_iter_instead_of_collect", - "clippy::if_let_mutex", - "clippy::implicit_clone", - "clippy::imprecise_flops", - "clippy::index_refutable_slice", - "clippy::inefficient_to_string", - "clippy::invalid_upcast_comparisons", - "clippy::iter_not_returning_iterator", - "clippy::large_digit_groups", - "clippy::large_stack_arrays", - "clippy::large_types_passed_by_value", - "clippy::let_unit_value", - "clippy::linkedlist", - "clippy::lossy_float_literal", - "clippy::macro_use_imports", - "clippy::manual_assert", - "clippy::manual_ok_or", - "clippy::map_err_ignore", - "clippy::map_flatten", - "clippy::map_unwrap_or", - "clippy::match_on_vec_items", - "clippy::match_same_arms", - "clippy::match_wild_err_arm", - "clippy::match_wildcard_for_single_variants", - "clippy::mem_forget", - "clippy::mismatched_target_os", - "clippy::missing_enforced_import_renames", - "clippy::missing_errors_doc", - "clippy::missing_safety_doc", - "clippy::mut_mut", - "clippy::mutex_integer", - "clippy::needless_borrow", - "clippy::needless_continue", - "clippy::needless_for_each", - "clippy::needless_pass_by_value", - "clippy::negative_feature_names", - "clippy::nonstandard_macro_braces", - "clippy::option_option", - "clippy::path_buf_push_overwrite", - "clippy::ptr_as_ptr", - "clippy::rc_mutex", - "clippy::ref_option_ref", - "clippy::rest_pat_in_fully_bound_structs", - "clippy::same_functions_in_if_condition", - "clippy::semicolon_if_nothing_returned", - "clippy::single_match_else", - "clippy::str_to_string", - "clippy::string_add_assign", - "clippy::string_add", - "clippy::string_lit_as_bytes", - "clippy::string_to_string", - "clippy::todo", - "clippy::trailing_empty_array", - "clippy::trait_duplication_in_bounds", - "clippy::unimplemented", - "clippy::unnecessary_wraps", - "clippy::unnested_or_patterns", - "clippy::unused_self", - "clippy::useless_transmute", - "clippy::verbose_file_reads", - "clippy::zero_sized_map_values", - "elided_lifetimes_in_paths", - "future_incompatible", - "nonstandard_style", - "rust_2018_idioms", - "rust_2021_prelude_collisions", - "rustdoc::missing_crate_level_docs", - "semicolon_in_expressions_from_macros", - "trivial_numeric_casts", - "unused_extern_crates", - "unused_import_braces", - "unused_lifetimes", - # "clippy::cloned_instead_of_copied", - # "clippy::mod_module_files", - # "trivial_casts", - # "unused_qualifications", + "clippy::all", + "clippy::await_holding_lock", + "clippy::bool_to_int_with_if", + "clippy::char_lit_as_u8", + "clippy::checked_conversions", + "clippy::cloned_instead_of_copied", + "clippy::dbg_macro", + "clippy::debug_assert_with_mut_call", + "clippy::derive_partial_eq_without_eq", + "clippy::disallowed_methods", + "clippy::disallowed_script_idents", + "clippy::doc_link_with_quotes", + "clippy::doc_markdown", + "clippy::empty_enum", + "clippy::enum_glob_use", + "clippy::equatable_if_let", + "clippy::exit", + "clippy::expl_impl_clone_on_copy", + "clippy::explicit_deref_methods", + "clippy::explicit_into_iter_loop", + "clippy::explicit_iter_loop", + "clippy::fallible_impl_from", + "clippy::filter_map_next", + "clippy::flat_map_option", + "clippy::float_cmp_const", + "clippy::fn_params_excessive_bools", + "clippy::fn_to_numeric_cast_any", + "clippy::from_iter_instead_of_collect", + "clippy::if_let_mutex", + "clippy::implicit_clone", + "clippy::imprecise_flops", + "clippy::index_refutable_slice", + "clippy::inefficient_to_string", + "clippy::invalid_upcast_comparisons", + "clippy::iter_not_returning_iterator", + "clippy::iter_on_empty_collections", + "clippy::iter_on_single_items", + "clippy::large_digit_groups", + "clippy::large_stack_arrays", + "clippy::large_types_passed_by_value", + "clippy::let_unit_value", + "clippy::linkedlist", + "clippy::lossy_float_literal", + "clippy::macro_use_imports", + "clippy::manual_assert", + "clippy::manual_instant_elapsed", + "clippy::manual_ok_or", + "clippy::manual_string_new", + "clippy::map_err_ignore", + "clippy::map_flatten", + "clippy::map_unwrap_or", + "clippy::match_on_vec_items", + "clippy::match_same_arms", + "clippy::match_wild_err_arm", + "clippy::match_wildcard_for_single_variants", + "clippy::mem_forget", + "clippy::mismatched_target_os", + "clippy::mismatching_type_param_order", + "clippy::missing_enforced_import_renames", + "clippy::missing_errors_doc", + "clippy::missing_safety_doc", + "clippy::mut_mut", + "clippy::mutex_integer", + "clippy::needless_borrow", + "clippy::needless_continue", + "clippy::needless_for_each", + "clippy::needless_pass_by_value", + "clippy::negative_feature_names", + "clippy::nonstandard_macro_braces", + "clippy::option_option", + "clippy::path_buf_push_overwrite", + "clippy::ptr_as_ptr", + "clippy::rc_mutex", + "clippy::ref_option_ref", + "clippy::rest_pat_in_fully_bound_structs", + "clippy::same_functions_in_if_condition", + "clippy::semicolon_if_nothing_returned", + "clippy::single_match_else", + "clippy::str_to_string", + "clippy::string_add_assign", + "clippy::string_add", + "clippy::string_lit_as_bytes", + "clippy::string_to_string", + "clippy::todo", + "clippy::trailing_empty_array", + "clippy::trait_duplication_in_bounds", + "clippy::unimplemented", + "clippy::unnecessary_wraps", + "clippy::unnested_or_patterns", + "clippy::unused_peekable", + "clippy::unused_rounding", + "clippy::unused_self", + "clippy::useless_transmute", + "clippy::verbose_file_reads", + "clippy::zero_sized_map_values", + "elided_lifetimes_in_paths", + "future_incompatible", + "nonstandard_style", + "rust_2018_idioms", + "rust_2021_prelude_collisions", + "rustdoc::missing_crate_level_docs", + "semicolon_in_expressions_from_macros", + "trivial_numeric_casts", + "unused_extern_crates", + "unused_import_braces", + "unused_lifetimes", ] allow = [ - "clippy::derive_partial_eq_without_eq", + "clippy::manual_range_contains", # This one is just annoying + + # Some of these we should try to put in "warn": "clippy::type_complexity", - "clippy::unnecessary_lazy_evaluations", - "clippy::let-and-return" + "clippy::undocumented_unsafe_blocks", + "trivial_casts", + "unsafe_op_in_unsafe_fn", # `unsafe_op_in_unsafe_fn` may become the default in future Rust versions: https://github.com/rust-lang/rust/issues/71668 + "unused_qualifications", ] diff --git a/README.md b/README.md index ac43a9ed..47e3cd48 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@ ๐Ÿ‘‰ [Click to run the web demo](https://www.egui.rs/#demo) ๐Ÿ‘ˆ -egui is a simple, fast, and highly portable immediate mode GUI library for Rust. egui runs on the web, natively, and [in your favorite game engine](#integrations) (or will soon). +egui (pronounced "e-gooey") is a simple, fast, and highly portable immediate mode GUI library for Rust. egui runs on the web, natively, and [in your favorite game engine](#integrations) (or will soon). egui aims to be the easiest-to-use Rust GUI library, and the simplest way to make a web app in Rust. @@ -158,17 +158,17 @@ An integration needs to do the following each frame: * **Input**: Gather input (mouse, touches, keyboard, screen size, etc) and give it to egui * Run the application code * **Output**: Handle egui output (cursor changes, paste, texture allocations, โ€ฆ) -* **Painting**: Render the triangle mesh egui produces (see [OpenGL example](https://github.com/emilk/egui/blob/master/crates/egui_glium/src/painter.rs)) +* **Painting**: Render the triangle mesh egui produces (see [OpenGL example](https://github.com/emilk/egui/blob/master/crates/egui_glow/src/painter.rs)) ### Official integrations These are the official egui integrations: -* [`eframe`](https://github.com/emilk/egui/tree/master/crates/eframe) for compiling the same app to web/wasm and desktop/native. Uses `egui_glow` and `egui-winit`. -* [`egui_glium`](https://github.com/emilk/egui/tree/master/crates/egui_glium) for compiling native apps with [Glium](https://github.com/glium/glium). +* [`eframe`](https://github.com/emilk/egui/tree/master/crates/eframe) for compiling the same app to web/wasm and desktop/native. Uses `egui-winit` and `egui_glow` or `egui-wgpu`. * [`egui_glow`](https://github.com/emilk/egui/tree/master/crates/egui_glow) for rendering egui with [glow](https://github.com/grovesNL/glow) on native and web, and for making native apps. * [`egui-wgpu`](https://github.com/emilk/egui/tree/master/crates/egui-wgpu) for [wgpu](https://crates.io/crates/wgpu) (WebGPU API). * [`egui-winit`](https://github.com/emilk/egui/tree/master/crates/egui-winit) for integrating with [winit](https://github.com/rust-windowing/winit). +* [`egui_glium`](https://github.com/emilk/egui/tree/master/crates/egui_glium) for compiling native apps with [Glium](https://github.com/glium/glium) (DEPRECATED - looking for new maintainer). ### 3rd party integrations @@ -186,12 +186,14 @@ These are the official egui integrations: * [`egui-tetra`](https://crates.io/crates/egui-tetra) for [Tetra](https://crates.io/crates/tetra), a 2D game framework. * [`egui-winit-ash-integration`](https://github.com/MatchaChoco010/egui-winit-ash-integration) for [winit](https://github.com/rust-windowing/winit) and [ash](https://github.com/MaikKlein/ash). * [`fltk-egui`](https://crates.io/crates/fltk-egui) for [fltk-rs](https://github.com/fltk-rs/fltk-rs). -* [`ggez-egui`](https://github.com/NemuiSen/ggez-egui) for the [ggez](https://ggez.rs/) game framework. +* [`ggegui`](https://github.com/NemuiSen/ggegui) for the [ggez](https://ggez.rs/) game framework. * [`godot-egui`](https://github.com/setzer22/godot-egui) for [godot-rust](https://github.com/godot-rust/godot-rust). * [`nannou_egui`](https://github.com/nannou-org/nannou/tree/master/nannou_egui) for [nannou](https://nannou.cc). * [`notan_egui`](https://github.com/Nazariglez/notan/tree/main/crates/notan_egui) for [notan](https://github.com/Nazariglez/notan). * [`screen-13-egui`](https://github.com/attackgoat/screen-13/tree/master/contrib/screen-13-egui) for [Screen 13](https://github.com/attackgoat/screen-13). +* [`egui_skia`](https://github.com/lucasmerlin/egui_skia) for [skia](https://github.com/rust-skia/rust-skia/tree/master/skia-safe). * [`smithay-egui`](https://github.com/Smithay/smithay-egui) for [smithay](https://github.com/Smithay/smithay/). +* [`tauri-egui`](https://github.com/tauri-apps/tauri-egui) for [tauri](https://github.com/tauri-apps/tauri). Missing an integration for the thing you're working on? Create one, it's easy! @@ -322,9 +324,9 @@ If you call `.await` in your GUI code, the UI will freeze, which is very bad UX. * [`tokio::sync::watch::channel`](https://docs.rs/tokio/latest/tokio/sync/watch/fn.channel.html) ### What about accessibility, such as screen readers? -There is experimental support for a screen reader. In [the web demo](https://www.egui.rs/#demo) you can enable it in the "Backend" tab. +egui includes optional support for [AccessKit](https://accesskit.dev/), which currently implements the native accessibility APIs on Windows and macOS. This feature is enabled by default in eframe. For platforms that AccessKit doesn't yet support, including web, there is an experimental built-in screen reader; in [the web demo](https://www.egui.rs/#demo) you can enable it in the "Backend" tab. -Read more at . +The original discussion of accessibility in egui is at . Now that AccessKit support is merged, providing a strong foundation for future accessibility work, please open new issues on specific accessibility problems. ### What is the difference between [egui](https://docs.rs/egui) and [eframe](https://github.com/emilk/egui/tree/master/crates/eframe)? @@ -370,6 +372,8 @@ egui uses the builder pattern for construction widgets. For instance: `ui.add(La Instead of using matching `begin/end` style function calls (which can be error prone) egui prefers to use `FnOnce` closures passed to a wrapping function. Lambdas are a bit ugly though, so I'd like to find a nicer solution to this. More discussion of this at . +egui uses a single `RwLock` for short-time locks on each access of `Context` data. This is to leave implementation simple and transactional and allow users to run their UI logic in parallel. Instead of creating mutex guards, egui uses closures passed to a wrapping function, e.g. `ctx.input(|i| i.key_down(Key::A))`. This is to make it less likely that a user would accidentally double-lock the `Context`, which would lead to a deadlock. + ### Inspiration The one and only [Dear ImGui](https://github.com/ocornut/imgui) is a great Immediate Mode GUI for C++ which works with many backends. That library revolutionized how I think about GUI code and turned GUI programming from something I hated to do to something I now enjoy. @@ -395,6 +399,7 @@ Notable contributions by: * [@mankinskin](https://github.com/mankinskin): [Context menus](https://github.com/emilk/egui/pull/543). * [@t18b219k](https://github.com/t18b219k): [Port glow painter to web](https://github.com/emilk/egui/pull/868). * [@danielkeller](https://github.com/danielkeller): [`Context` refactor](https://github.com/emilk/egui/pull/1050). +* [@MaximOsipenko](https://github.com/MaximOsipenko): [`Context` lock refactor](https://github.com/emilk/egui/pull/2625). * And [many more](https://github.com/emilk/egui/graphs/contributors?type=a). egui is licensed under [MIT](LICENSE-MIT) OR [Apache-2.0](LICENSE-APACHE). @@ -407,3 +412,12 @@ Default fonts: * `Hack-Regular.ttf`: , [MIT Licence](https://github.com/source-foundry/Hack/blob/master/LICENSE.md) * `NotoEmoji-Regular.ttf`: [google.com/get/noto](https://google.com/get/noto), [SIL Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL) * `Ubuntu-Light.ttf` by [Dalton Maag](http://www.daltonmaag.com/): [Ubuntu font licence](https://ubuntu.com/legal/font-licence) + +--- + +
+ + +egui development is sponsored by [Rerun](https://www.rerun.io/), a startup doing
+visualizations for computer vision and robotics. +
diff --git a/clippy.toml b/clippy.toml new file mode 100644 index 00000000..f09ee277 --- /dev/null +++ b/clippy.toml @@ -0,0 +1 @@ +doc-valid-idents = ["AccessKit", ".."] diff --git a/crates/ecolor/CHANGELOG.md b/crates/ecolor/CHANGELOG.md new file mode 100644 index 00000000..6dcc6873 --- /dev/null +++ b/crates/ecolor/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog for ecolor +All notable changes to the `ecolor` crate will be noted in this file. + + +## Unreleased +* Add `Color32::gamma_multiply` ([#2437](https://github.com/emilk/egui/pull/2437)). + + +## 0.20.0 - 2022-12-08 +* Split out `ecolor` crate from `epaint` diff --git a/crates/ecolor/Cargo.toml b/crates/ecolor/Cargo.toml new file mode 100644 index 00000000..ac0aa458 --- /dev/null +++ b/crates/ecolor/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "ecolor" +version = "0.20.0" +authors = [ + "Emil Ernerfeldt ", + "Andreas Reich ", +] +description = "Color structs and color conversion utilities" +edition = "2021" +rust-version = "1.65" +homepage = "https://github.com/emilk/egui" +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/emilk/egui" +categories = ["mathematics", "encoding"] +keywords = ["gui", "color", "conversion", "gamedev", "images"] +include = ["../LICENSE-APACHE", "../LICENSE-MIT", "**/*.rs", "Cargo.toml"] + +[package.metadata.docs.rs] +all-features = true + +[lib] + + +[features] +default = [] + +## Enable additional checks if debug assertions are enabled (debug builds). +extra_debug_asserts = [] +## Always enable additional checks. +extra_asserts = [] + + +[dependencies] +#! ### Optional dependencies + +## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast `ecolor` types to `&[u8]`. +bytemuck = { version = "1.7.2", optional = true, features = ["derive"] } + +## [`cint`](https://docs.rs/cint) enables interopability with other color libraries. +cint = { version = "0.3.1", optional = true } + +## Enable the [`hex_color`] macro. +color-hex = { version = "0.2.0", optional = true } + +## Enable this when generating docs. +document-features = { version = "0.2", optional = true } + +## Allow serialization using [`serde`](https://docs.rs/serde). +serde = { version = "1", optional = true, features = ["derive"] } diff --git a/crates/ecolor/README.md b/crates/ecolor/README.md new file mode 100644 index 00000000..5dee0776 --- /dev/null +++ b/crates/ecolor/README.md @@ -0,0 +1,5 @@ +# ecolor - egui color library + +A simple color storage and conversion library. + +Made for [`egui`](https://github.com/emilk/egui/). diff --git a/crates/ecolor/src/cint_impl.rs b/crates/ecolor/src/cint_impl.rs new file mode 100644 index 00000000..a730fda4 --- /dev/null +++ b/crates/ecolor/src/cint_impl.rs @@ -0,0 +1,161 @@ +use super::*; +use cint::{Alpha, ColorInterop, EncodedSrgb, Hsv, LinearSrgb, PremultipliedAlpha}; + +// ---- Color32 ---- + +impl From>> for Color32 { + fn from(srgba: Alpha>) -> Self { + let Alpha { + color: EncodedSrgb { r, g, b }, + alpha: a, + } = srgba; + + Color32::from_rgba_unmultiplied(r, g, b, a) + } +} + +// No From for Alpha<_> because Color32 is premultiplied + +impl From>> for Color32 { + fn from(srgba: PremultipliedAlpha>) -> Self { + let PremultipliedAlpha { + color: EncodedSrgb { r, g, b }, + alpha: a, + } = srgba; + + Color32::from_rgba_premultiplied(r, g, b, a) + } +} + +impl From for PremultipliedAlpha> { + fn from(col: Color32) -> Self { + let (r, g, b, a) = col.to_tuple(); + + PremultipliedAlpha { + color: EncodedSrgb { r, g, b }, + alpha: a, + } + } +} + +impl From>> for Color32 { + fn from(srgba: PremultipliedAlpha>) -> Self { + let PremultipliedAlpha { + color: EncodedSrgb { r, g, b }, + alpha: a, + } = srgba; + + // This is a bit of an abuse of the function name but it does what we want. + let r = linear_u8_from_linear_f32(r); + let g = linear_u8_from_linear_f32(g); + let b = linear_u8_from_linear_f32(b); + let a = linear_u8_from_linear_f32(a); + + Color32::from_rgba_premultiplied(r, g, b, a) + } +} + +impl From for PremultipliedAlpha> { + fn from(col: Color32) -> Self { + let (r, g, b, a) = col.to_tuple(); + + // This is a bit of an abuse of the function name but it does what we want. + let r = linear_f32_from_linear_u8(r); + let g = linear_f32_from_linear_u8(g); + let b = linear_f32_from_linear_u8(b); + let a = linear_f32_from_linear_u8(a); + + PremultipliedAlpha { + color: EncodedSrgb { r, g, b }, + alpha: a, + } + } +} + +impl ColorInterop for Color32 { + type CintTy = PremultipliedAlpha>; +} + +// ---- Rgba ---- + +impl From>> for Rgba { + fn from(srgba: PremultipliedAlpha>) -> Self { + let PremultipliedAlpha { + color: LinearSrgb { r, g, b }, + alpha: a, + } = srgba; + + Rgba([r, g, b, a]) + } +} + +impl From for PremultipliedAlpha> { + fn from(col: Rgba) -> Self { + let (r, g, b, a) = col.to_tuple(); + + PremultipliedAlpha { + color: LinearSrgb { r, g, b }, + alpha: a, + } + } +} + +impl ColorInterop for Rgba { + type CintTy = PremultipliedAlpha>; +} + +// ---- Hsva ---- + +impl From>> for Hsva { + fn from(srgba: Alpha>) -> Self { + let Alpha { + color: Hsv { h, s, v }, + alpha: a, + } = srgba; + + Hsva::new(h, s, v, a) + } +} + +impl From for Alpha> { + fn from(col: Hsva) -> Self { + let Hsva { h, s, v, a } = col; + + Alpha { + color: Hsv { h, s, v }, + alpha: a, + } + } +} + +impl ColorInterop for Hsva { + type CintTy = Alpha>; +} + +// ---- HsvaGamma ---- + +impl ColorInterop for HsvaGamma { + type CintTy = Alpha>; +} + +impl From>> for HsvaGamma { + fn from(srgba: Alpha>) -> Self { + let Alpha { + color: Hsv { h, s, v }, + alpha: a, + } = srgba; + + Hsva::new(h, s, v, a).into() + } +} + +impl From for Alpha> { + fn from(col: HsvaGamma) -> Self { + let Hsva { h, s, v, a } = col.into(); + + Alpha { + color: Hsv { h, s, v }, + alpha: a, + } + } +} diff --git a/crates/ecolor/src/color32.rs b/crates/ecolor/src/color32.rs new file mode 100644 index 00000000..09e68116 --- /dev/null +++ b/crates/ecolor/src/color32.rs @@ -0,0 +1,216 @@ +use crate::{gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_linear_u8, Rgba}; + +/// This format is used for space-efficient color representation (32 bits). +/// +/// 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. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))] +pub struct Color32(pub(crate) [u8; 4]); + +impl std::ops::Index for Color32 { + type Output = u8; + + #[inline(always)] + fn index(&self, index: usize) -> &u8 { + &self.0[index] + } +} + +impl std::ops::IndexMut for Color32 { + #[inline(always)] + fn index_mut(&mut self, index: usize) -> &mut u8 { + &mut self.0[index] + } +} + +impl Color32 { + // Mostly follows CSS names: + + pub const TRANSPARENT: Color32 = Color32::from_rgba_premultiplied(0, 0, 0, 0); + pub const BLACK: Color32 = Color32::from_rgb(0, 0, 0); + pub const DARK_GRAY: Color32 = Color32::from_rgb(96, 96, 96); + pub const GRAY: Color32 = Color32::from_rgb(160, 160, 160); + pub const LIGHT_GRAY: Color32 = Color32::from_rgb(220, 220, 220); + pub const WHITE: Color32 = Color32::from_rgb(255, 255, 255); + + pub const BROWN: Color32 = Color32::from_rgb(165, 42, 42); + pub const DARK_RED: Color32 = Color32::from_rgb(0x8B, 0, 0); + pub const RED: Color32 = Color32::from_rgb(255, 0, 0); + pub const LIGHT_RED: Color32 = Color32::from_rgb(255, 128, 128); + + pub const YELLOW: Color32 = Color32::from_rgb(255, 255, 0); + pub const LIGHT_YELLOW: Color32 = Color32::from_rgb(255, 255, 0xE0); + pub const KHAKI: Color32 = Color32::from_rgb(240, 230, 140); + + pub const DARK_GREEN: Color32 = Color32::from_rgb(0, 0x64, 0); + pub const GREEN: Color32 = Color32::from_rgb(0, 255, 0); + pub const LIGHT_GREEN: Color32 = Color32::from_rgb(0x90, 0xEE, 0x90); + + pub const DARK_BLUE: Color32 = Color32::from_rgb(0, 0, 0x8B); + pub const BLUE: Color32 = Color32::from_rgb(0, 0, 255); + pub const LIGHT_BLUE: Color32 = Color32::from_rgb(0xAD, 0xD8, 0xE6); + + pub const GOLD: Color32 = Color32::from_rgb(255, 215, 0); + + pub const DEBUG_COLOR: Color32 = Color32::from_rgba_premultiplied(0, 200, 0, 128); + + /// An ugly color that is planned to be replaced before making it to the screen. + pub const TEMPORARY_COLOR: Color32 = Color32::from_rgb(64, 254, 0); + + #[inline(always)] + pub const fn from_rgb(r: u8, g: u8, b: u8) -> Self { + Self([r, g, b, 255]) + } + + #[inline(always)] + pub const fn from_rgb_additive(r: u8, g: u8, b: u8) -> Self { + Self([r, g, b, 0]) + } + + /// From `sRGBA` with premultiplied alpha. + #[inline(always)] + 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. + pub fn from_rgba_unmultiplied(r: u8, g: u8, b: u8, a: u8) -> Self { + if a == 255 { + Self::from_rgb(r, g, b) // common-case optimization + } else if a == 0 { + Self::TRANSPARENT // common-case optimization + } else { + let r_lin = linear_f32_from_gamma_u8(r); + let g_lin = linear_f32_from_gamma_u8(g); + let b_lin = linear_f32_from_gamma_u8(b); + let a_lin = linear_f32_from_linear_u8(a); + + let r = gamma_u8_from_linear_f32(r_lin * a_lin); + let g = gamma_u8_from_linear_f32(g_lin * a_lin); + let b = gamma_u8_from_linear_f32(b_lin * a_lin); + + Self::from_rgba_premultiplied(r, g, b, a) + } + } + + #[inline(always)] + pub const fn from_gray(l: u8) -> Self { + Self([l, l, l, 255]) + } + + #[inline(always)] + pub const fn from_black_alpha(a: u8) -> Self { + Self([0, 0, 0, a]) + } + + pub fn from_white_alpha(a: u8) -> Self { + Rgba::from_white_alpha(linear_f32_from_linear_u8(a)).into() + } + + #[inline(always)] + pub const fn from_additive_luminance(l: u8) -> Self { + Self([l, l, l, 0]) + } + + #[inline(always)] + pub const fn is_opaque(&self) -> bool { + self.a() == 255 + } + + #[inline(always)] + pub const fn r(&self) -> u8 { + self.0[0] + } + + #[inline(always)] + pub const fn g(&self) -> u8 { + self.0[1] + } + + #[inline(always)] + pub const fn b(&self) -> u8 { + self.0[2] + } + + #[inline(always)] + pub const fn a(&self) -> u8 { + self.0[3] + } + + /// Returns an opaque version of self + pub fn to_opaque(self) -> Self { + Rgba::from(self).to_opaque().into() + } + + /// Returns an additive version of self + #[inline(always)] + pub const fn additive(self) -> Self { + let [r, g, b, _] = self.to_array(); + Self([r, g, b, 0]) + } + + /// Premultiplied RGBA + #[inline(always)] + pub const fn to_array(&self) -> [u8; 4] { + [self.r(), self.g(), self.b(), self.a()] + } + + /// Premultiplied RGBA + #[inline(always)] + pub const fn to_tuple(&self) -> (u8, u8, u8, u8) { + (self.r(), self.g(), self.b(), self.a()) + } + + pub fn to_srgba_unmultiplied(&self) -> [u8; 4] { + Rgba::from(*self).to_srgba_unmultiplied() + } + + /// Multiply with 0.5 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(self, factor: f32) -> Color32 { + crate::ecolor_assert!(0.0 <= factor && factor <= 1.0); + let Self([r, g, b, a]) = self; + Self([ + (r as f32 * factor + 0.5) as u8, + (g as f32 * factor + 0.5) as u8, + (b as f32 * factor + 0.5) as u8, + (a as f32 * factor + 0.5) 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 may want to use [`Self::gamma_multiply`] instead. + pub fn linear_multiply(self, factor: f32) -> Color32 { + crate::ecolor_assert!(0.0 <= factor && factor <= 1.0); + // 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() + } + + /// Converts to floating point values in the range 0-1 without any gamma space conversion. + /// + /// Use this with great care! In almost all cases, you want to convert to [`crate::Rgba`] instead + /// in order to obtain linear space color values. + #[inline] + pub fn to_normalized_gamma_f32(self) -> [f32; 4] { + let Self([r, g, b, a]) = self; + [ + r as f32 / 255.0, + g as f32 / 255.0, + b as f32 / 255.0, + a as f32 / 255.0, + ] + } +} diff --git a/crates/ecolor/src/hex_color_macro.rs b/crates/ecolor/src/hex_color_macro.rs new file mode 100644 index 00000000..450e6a86 --- /dev/null +++ b/crates/ecolor/src/hex_color_macro.rs @@ -0,0 +1,39 @@ +/// Construct a [`crate::Color32`] from a hex RGB or RGBA string. +/// +/// ``` +/// # use ecolor::{hex_color, Color32}; +/// assert_eq!(hex_color!("#202122"), Color32::from_rgb(0x20, 0x21, 0x22)); +/// assert_eq!(hex_color!("#abcdef12"), Color32::from_rgba_unmultiplied(0xab, 0xcd, 0xef, 0x12)); +/// ``` +#[macro_export] +macro_rules! hex_color { + ($s:literal) => {{ + let array = color_hex::color_from_hex!($s); + if array.len() == 3 { + $crate::Color32::from_rgb(array[0], array[1], array[2]) + } else { + #[allow(unconditional_panic)] + $crate::Color32::from_rgba_unmultiplied(array[0], array[1], array[2], array[3]) + } + }}; +} + +#[test] +fn test_from_rgb_hex() { + assert_eq!( + crate::Color32::from_rgb(0x20, 0x21, 0x22), + hex_color!("#202122") + ); + assert_eq!( + crate::Color32::from_rgb_additive(0x20, 0x21, 0x22), + hex_color!("#202122").additive() + ); +} + +#[test] +fn test_from_rgba_hex() { + assert_eq!( + crate::Color32::from_rgba_unmultiplied(0x20, 0x21, 0x22, 0x50), + hex_color!("20212250") + ); +} diff --git a/crates/ecolor/src/hsva.rs b/crates/ecolor/src/hsva.rs new file mode 100644 index 00000000..8a68cb93 --- /dev/null +++ b/crates/ecolor/src/hsva.rs @@ -0,0 +1,231 @@ +use crate::{ + gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_linear_u8, + linear_u8_from_linear_f32, Color32, Rgba, +}; + +/// Hue, saturation, value, alpha. All in the range [0, 1]. +/// No premultiplied alpha. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct Hsva { + /// hue 0-1 + pub h: f32, + + /// saturation 0-1 + pub s: f32, + + /// value 0-1 + pub v: f32, + + /// alpha 0-1. A negative value signifies an additive color (and alpha is ignored). + pub a: f32, +} + +impl Hsva { + pub fn new(h: f32, s: f32, v: f32, a: f32) -> Self { + Self { h, s, v, a } + } + + /// From `sRGBA` with premultiplied alpha + pub fn from_srgba_premultiplied(srgba: [u8; 4]) -> Self { + Self::from_rgba_premultiplied( + linear_f32_from_gamma_u8(srgba[0]), + linear_f32_from_gamma_u8(srgba[1]), + linear_f32_from_gamma_u8(srgba[2]), + linear_f32_from_linear_u8(srgba[3]), + ) + } + + /// From `sRGBA` without premultiplied alpha + pub fn from_srgba_unmultiplied(srgba: [u8; 4]) -> Self { + Self::from_rgba_unmultiplied( + linear_f32_from_gamma_u8(srgba[0]), + linear_f32_from_gamma_u8(srgba[1]), + linear_f32_from_gamma_u8(srgba[2]), + linear_f32_from_linear_u8(srgba[3]), + ) + } + + /// From linear RGBA with premultiplied alpha + 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 r == 0.0 && b == 0.0 && a == 0.0 { + Hsva::default() + } else { + Hsva::from_additive_rgb([r, g, b]) + } + } else { + let (h, s, v) = hsv_from_rgb([r / a, g / a, b / a]); + Hsva { h, s, v, a } + } + } + + /// From linear RGBA without premultiplied alpha + pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { + #![allow(clippy::many_single_char_names)] + let (h, s, v) = hsv_from_rgb([r, g, b]); + Hsva { h, s, v, a } + } + + pub fn from_additive_rgb(rgb: [f32; 3]) -> Self { + let (h, s, v) = hsv_from_rgb(rgb); + Hsva { + h, + s, + v, + a: -0.5, // anything negative is treated as additive + } + } + + pub fn from_rgb(rgb: [f32; 3]) -> Self { + let (h, s, v) = hsv_from_rgb(rgb); + Hsva { h, s, v, a: 1.0 } + } + + pub fn from_srgb([r, g, b]: [u8; 3]) -> Self { + Self::from_rgb([ + linear_f32_from_gamma_u8(r), + linear_f32_from_gamma_u8(g), + linear_f32_from_gamma_u8(b), + ]) + } + + // ------------------------------------------------------------------------ + + pub fn to_opaque(self) -> Self { + Self { a: 1.0, ..self } + } + + pub fn to_rgb(&self) -> [f32; 3] { + rgb_from_hsv((self.h, self.s, self.v)) + } + + pub fn to_srgb(&self) -> [u8; 3] { + let [r, g, b] = self.to_rgb(); + [ + gamma_u8_from_linear_f32(r), + gamma_u8_from_linear_f32(g), + gamma_u8_from_linear_f32(b), + ] + } + + pub fn to_rgba_premultiplied(&self) -> [f32; 4] { + let [r, g, b, a] = self.to_rgba_unmultiplied(); + let additive = a < 0.0; + if additive { + [r, g, b, 0.0] + } else { + [a * r, a * g, a * b, a] + } + } + + /// Represents additive colors using a negative alpha. + pub fn to_rgba_unmultiplied(&self) -> [f32; 4] { + let Hsva { h, s, v, a } = *self; + let [r, g, b] = rgb_from_hsv((h, s, v)); + [r, g, b, a] + } + + 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), + ] + } + + 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()), + ] + } +} + +impl From for Rgba { + fn from(hsva: Hsva) -> Rgba { + Rgba(hsva.to_rgba_premultiplied()) + } +} + +impl From for Hsva { + fn from(rgba: Rgba) -> Hsva { + Self::from_rgba_premultiplied(rgba.0[0], rgba.0[1], rgba.0[2], rgba.0[3]) + } +} + +impl From for Color32 { + fn from(hsva: Hsva) -> Color32 { + Color32::from(Rgba::from(hsva)) + } +} + +impl From for Hsva { + fn from(srgba: Color32) -> Hsva { + Hsva::from(Rgba::from(srgba)) + } +} + +/// All ranges in 0-1, rgb is linear. +pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) { + #![allow(clippy::many_single_char_names)] + let min = r.min(g.min(b)); + let max = r.max(g.max(b)); // value + + let range = max - min; + + let h = if max == min { + 0.0 // hue is undefined + } else if max == r { + (g - b) / (6.0 * range) + } else if max == g { + (b - r) / (6.0 * range) + 1.0 / 3.0 + } else { + // max == b + (r - g) / (6.0 * range) + 2.0 / 3.0 + }; + let h = (h + 1.0).fract(); // wrap + let s = if max == 0.0 { 0.0 } else { 1.0 - min / max }; + (h, s, max) +} + +/// All ranges in 0-1, rgb is linear. +pub fn rgb_from_hsv((h, s, v): (f32, f32, f32)) -> [f32; 3] { + #![allow(clippy::many_single_char_names)] + let h = (h.fract() + 1.0).fract(); // wrap + let s = s.clamp(0.0, 1.0); + + let f = h * 6.0 - (h * 6.0).floor(); + let p = v * (1.0 - s); + let q = v * (1.0 - f * s); + let t = v * (1.0 - (1.0 - f) * s); + + match (h * 6.0).floor() as i32 % 6 { + 0 => [v, t, p], + 1 => [q, v, p], + 2 => [p, v, t], + 3 => [p, q, v], + 4 => [t, p, v], + 5 => [v, p, q], + _ => unreachable!(), + } +} + +#[test] +#[ignore] // a bit expensive +fn test_hsv_roundtrip() { + for r in 0..=255 { + for g in 0..=255 { + for b in 0..=255 { + let srgba = Color32::from_rgb(r, g, b); + let hsva = Hsva::from(srgba); + assert_eq!(srgba, Color32::from(hsva)); + } + } + } +} diff --git a/crates/ecolor/src/hsva_gamma.rs b/crates/ecolor/src/hsva_gamma.rs new file mode 100644 index 00000000..3135ef10 --- /dev/null +++ b/crates/ecolor/src/hsva_gamma.rs @@ -0,0 +1,66 @@ +use crate::{gamma_from_linear, linear_from_gamma, Color32, Hsva, Rgba}; + +/// Like Hsva but with the `v` value (brightness) being gamma corrected +/// so that it is somewhat perceptually even. +#[derive(Clone, Copy, Debug, Default, PartialEq)] +pub struct HsvaGamma { + /// hue 0-1 + pub h: f32, + + /// saturation 0-1 + pub s: f32, + + /// value 0-1, in gamma-space (~perceptually even) + pub v: f32, + + /// alpha 0-1. A negative value signifies an additive color (and alpha is ignored). + pub a: f32, +} + +impl From for Rgba { + fn from(hsvag: HsvaGamma) -> Rgba { + Hsva::from(hsvag).into() + } +} + +impl From for Color32 { + fn from(hsvag: HsvaGamma) -> Color32 { + Rgba::from(hsvag).into() + } +} + +impl From for Hsva { + fn from(hsvag: HsvaGamma) -> Hsva { + let HsvaGamma { h, s, v, a } = hsvag; + Hsva { + h, + s, + v: linear_from_gamma(v), + a, + } + } +} + +impl From for HsvaGamma { + fn from(rgba: Rgba) -> HsvaGamma { + Hsva::from(rgba).into() + } +} + +impl From for HsvaGamma { + fn from(srgba: Color32) -> HsvaGamma { + Hsva::from(srgba).into() + } +} + +impl From for HsvaGamma { + fn from(hsva: Hsva) -> HsvaGamma { + let Hsva { h, s, v, a } = hsva; + HsvaGamma { + h, + s, + v: gamma_from_linear(v), + a, + } + } +} diff --git a/crates/ecolor/src/lib.rs b/crates/ecolor/src/lib.rs new file mode 100644 index 00000000..9bc42c4c --- /dev/null +++ b/crates/ecolor/src/lib.rs @@ -0,0 +1,173 @@ +//! Color conversions and types. +//! +//! If you want a compact color representation, use [`Color32`]. +//! If you want to manipulate RGBA colors use [`Rgba`]. +//! If you want to manipulate colors in a way closer to how humans think about colors, use [`HsvaGamma`]. +//! +//! ## Feature flags +#![cfg_attr(feature = "document-features", doc = document_features::document_features!())] +//! + +#![allow(clippy::wrong_self_convention)] + +#[cfg(feature = "cint")] +mod cint_impl; +#[cfg(feature = "cint")] +pub use cint_impl::*; + +mod color32; +pub use color32::*; + +mod hsva_gamma; +pub use hsva_gamma::*; + +mod hsva; +pub use hsva::*; + +#[cfg(feature = "color-hex")] +mod hex_color_macro; + +mod rgba; +pub use rgba::*; + +// ---------------------------------------------------------------------------- +// Color conversion: + +impl From for Rgba { + fn from(srgba: Color32) -> Rgba { + Rgba([ + 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]), + ]) + } +} + +impl From for Color32 { + fn from(rgba: Rgba) -> Color32 { + Color32([ + 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]), + ]) + } +} + +/// gamma [0, 255] -> linear [0, 1]. +pub fn linear_f32_from_gamma_u8(s: u8) -> f32 { + if s <= 10 { + s as f32 / 3294.6 + } else { + ((s as f32 + 14.025) / 269.025).powf(2.4) + } +} + +/// linear [0, 255] -> linear [0, 1]. +/// Useful for alpha-channel. +#[inline(always)] +pub fn linear_f32_from_linear_u8(a: u8) -> f32 { + a as f32 / 255.0 +} + +/// linear [0, 1] -> gamma [0, 255] (clamped). +/// Values outside this range will be clamped to the range. +pub fn gamma_u8_from_linear_f32(l: f32) -> u8 { + if l <= 0.0 { + 0 + } else if l <= 0.0031308 { + fast_round(3294.6 * l) + } else if l <= 1.0 { + fast_round(269.025 * l.powf(1.0 / 2.4) - 14.025) + } else { + 255 + } +} + +/// linear [0, 1] -> linear [0, 255] (clamped). +/// Useful for alpha-channel. +#[inline(always)] +pub fn linear_u8_from_linear_f32(a: f32) -> u8 { + fast_round(a * 255.0) +} + +fn fast_round(r: f32) -> u8 { + (r + 0.5).floor() as _ // rust does a saturating cast since 1.45 +} + +#[test] +pub fn test_srgba_conversion() { + for b in 0..=255 { + let l = linear_f32_from_gamma_u8(b); + assert!(0.0 <= l && l <= 1.0); + assert_eq!(gamma_u8_from_linear_f32(l), b); + } +} + +/// gamma [0, 1] -> linear [0, 1] (not clamped). +/// Works for numbers outside this range (e.g. negative numbers). +pub fn linear_from_gamma(gamma: f32) -> f32 { + if gamma < 0.0 { + -linear_from_gamma(-gamma) + } else if gamma <= 0.04045 { + gamma / 12.92 + } else { + ((gamma + 0.055) / 1.055).powf(2.4) + } +} + +/// linear [0, 1] -> gamma [0, 1] (not clamped). +/// Works for numbers outside this range (e.g. negative numbers). +pub fn gamma_from_linear(linear: f32) -> f32 { + if linear < 0.0 { + -gamma_from_linear(-linear) + } else if linear <= 0.0031308 { + 12.92 * linear + } else { + 1.055 * linear.powf(1.0 / 2.4) - 0.055 + } +} + +// ---------------------------------------------------------------------------- + +/// An assert that is only active when `epaint` is compiled with the `extra_asserts` feature +/// or with the `extra_debug_asserts` feature in debug builds. +#[macro_export] +macro_rules! ecolor_assert { + ($($arg: tt)*) => { + if cfg!(any( + feature = "extra_asserts", + all(feature = "extra_debug_asserts", debug_assertions), + )) { + assert!($($arg)*); + } + } +} + +// ---------------------------------------------------------------------------- + +/// Cheap and ugly. +/// Made for graying out disabled `Ui`s. +pub fn tint_color_towards(color: Color32, target: Color32) -> Color32 { + let [mut r, mut g, mut b, mut a] = color.to_array(); + + if a == 0 { + r /= 2; + g /= 2; + b /= 2; + } else if a < 170 { + // Cheapish and looks ok. + // Works for e.g. grid stripes. + let div = (2 * 255 / a as i32) as u8; + r = r / 2 + target.r() / div; + g = g / 2 + target.g() / div; + b = b / 2 + target.b() / div; + a /= 2; + } else { + r = r / 2 + target.r() / 2; + g = g / 2 + target.g() / 2; + b = b / 2 + target.b() / 2; + } + Color32::from_rgba_premultiplied(r, g, b, a) +} diff --git a/crates/ecolor/src/rgba.rs b/crates/ecolor/src/rgba.rs new file mode 100644 index 00000000..f9d671fd --- /dev/null +++ b/crates/ecolor/src/rgba.rs @@ -0,0 +1,266 @@ +use crate::{ + gamma_u8_from_linear_f32, linear_f32_from_gamma_u8, linear_f32_from_linear_u8, + linear_u8_from_linear_f32, +}; + +/// 0-1 linear space `RGBA` color with premultiplied alpha. +#[repr(C)] +#[derive(Clone, Copy, Debug, Default, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))] +pub struct Rgba(pub(crate) [f32; 4]); + +impl std::ops::Index for Rgba { + type Output = f32; + + #[inline(always)] + fn index(&self, index: usize) -> &f32 { + &self.0[index] + } +} + +impl std::ops::IndexMut for Rgba { + #[inline(always)] + fn index_mut(&mut self, index: usize) -> &mut f32 { + &mut self.0[index] + } +} + +#[inline(always)] +pub(crate) fn f32_hash(state: &mut H, f: f32) { + if f == 0.0 { + state.write_u8(0); + } else if f.is_nan() { + state.write_u8(1); + } else { + use std::hash::Hash; + f.to_bits().hash(state); + } +} + +#[allow(clippy::derive_hash_xor_eq)] +impl std::hash::Hash for Rgba { + #[inline] + fn hash(&self, state: &mut H) { + crate::f32_hash(state, self.0[0]); + crate::f32_hash(state, self.0[1]); + crate::f32_hash(state, self.0[2]); + crate::f32_hash(state, self.0[3]); + } +} + +impl Rgba { + pub const TRANSPARENT: Rgba = Rgba::from_rgba_premultiplied(0.0, 0.0, 0.0, 0.0); + pub const BLACK: Rgba = Rgba::from_rgb(0.0, 0.0, 0.0); + pub const WHITE: Rgba = Rgba::from_rgb(1.0, 1.0, 1.0); + pub const RED: Rgba = Rgba::from_rgb(1.0, 0.0, 0.0); + pub const GREEN: Rgba = Rgba::from_rgb(0.0, 1.0, 0.0); + pub const BLUE: Rgba = Rgba::from_rgb(0.0, 0.0, 1.0); + + #[inline(always)] + pub const fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { + Self([r, g, b, a]) + } + + #[inline(always)] + pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { + Self([r * a, g * a, b * a, a]) + } + + #[inline(always)] + 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) + } + + #[inline(always)] + 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) + } + + #[inline(always)] + pub const fn from_rgb(r: f32, g: f32, b: f32) -> Self { + Self([r, g, b, 1.0]) + } + + #[inline(always)] + pub const fn from_gray(l: f32) -> Self { + Self([l, l, l, 1.0]) + } + + pub fn from_luminance_alpha(l: f32, a: f32) -> Self { + crate::ecolor_assert!(0.0 <= l && l <= 1.0); + crate::ecolor_assert!(0.0 <= a && a <= 1.0); + Self([l * a, l * a, l * a, a]) + } + + /// Transparent black + #[inline(always)] + pub fn from_black_alpha(a: f32) -> Self { + crate::ecolor_assert!(0.0 <= a && a <= 1.0); + Self([0.0, 0.0, 0.0, a]) + } + + /// Transparent white + #[inline(always)] + pub fn from_white_alpha(a: f32) -> Self { + crate::ecolor_assert!(0.0 <= a && a <= 1.0, "a: {}", a); + Self([a, a, a, a]) + } + + /// Return an additive version of this color (alpha = 0) + #[inline(always)] + pub fn additive(self) -> Self { + let [r, g, b, _] = self.0; + Self([r, g, b, 0.0]) + } + + /// Multiply with e.g. 0.5 to make us half transparent + #[inline(always)] + pub fn multiply(self, alpha: f32) -> Self { + Self([ + alpha * self[0], + alpha * self[1], + alpha * self[2], + alpha * self[3], + ]) + } + + #[inline(always)] + pub fn r(&self) -> f32 { + self.0[0] + } + + #[inline(always)] + pub fn g(&self) -> f32 { + self.0[1] + } + + #[inline(always)] + pub fn b(&self) -> f32 { + self.0[2] + } + + #[inline(always)] + pub fn a(&self) -> f32 { + self.0[3] + } + + /// How perceptually intense (bright) is the color? + #[inline] + pub fn intensity(&self) -> f32 { + 0.3 * self.r() + 0.59 * self.g() + 0.11 * self.b() + } + + /// Returns an opaque version of self + pub fn to_opaque(&self) -> Self { + if self.a() == 0.0 { + // Additive or fully transparent black. + Self::from_rgb(self.r(), self.g(), self.b()) + } else { + // un-multiply alpha: + Self::from_rgb( + self.r() / self.a(), + self.g() / self.a(), + self.b() / self.a(), + ) + } + } + + /// Premultiplied RGBA + #[inline(always)] + pub fn to_array(&self) -> [f32; 4] { + [self.r(), self.g(), self.b(), self.a()] + } + + /// Premultiplied RGBA + #[inline(always)] + pub fn to_tuple(&self) -> (f32, f32, f32, f32) { + (self.r(), self.g(), self.b(), self.a()) + } + + /// unmultiply the alpha + pub fn to_rgba_unmultiplied(&self) -> [f32; 4] { + let a = self.a(); + if a == 0.0 { + // Additive, let's assume we are black + self.0 + } else { + [self.r() / a, self.g() / a, self.b() / a, a] + } + } + + /// unmultiply the alpha + 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()), + ] + } +} + +impl std::ops::Add for Rgba { + type Output = Rgba; + + #[inline(always)] + fn add(self, rhs: Rgba) -> Rgba { + Rgba([ + self[0] + rhs[0], + self[1] + rhs[1], + self[2] + rhs[2], + self[3] + rhs[3], + ]) + } +} + +impl std::ops::Mul for Rgba { + type Output = Rgba; + + #[inline(always)] + fn mul(self, other: Rgba) -> Rgba { + Rgba([ + self[0] * other[0], + self[1] * other[1], + self[2] * other[2], + self[3] * other[3], + ]) + } +} + +impl std::ops::Mul for Rgba { + type Output = Rgba; + + #[inline(always)] + fn mul(self, factor: f32) -> Rgba { + Rgba([ + self[0] * factor, + self[1] * factor, + self[2] * factor, + self[3] * factor, + ]) + } +} + +impl std::ops::Mul for f32 { + type Output = Rgba; + + #[inline(always)] + fn mul(self, rgba: Rgba) -> Rgba { + Rgba([ + self * rgba[0], + self * rgba[1], + self * rgba[2], + self * rgba[3], + ]) + } +} diff --git a/crates/eframe/CHANGELOG.md b/crates/eframe/CHANGELOG.md index 31f81715..32375fad 100644 --- a/crates/eframe/CHANGELOG.md +++ b/crates/eframe/CHANGELOG.md @@ -5,18 +5,45 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C ## Unreleased -* Added `NativeOptions::fullsize_content` option on Mac to build titlebar-less windows with floating window controls ([#2049](https://github.com/emilk/egui/pull/2049)). +* โš ๏ธ BREAKING: `App::clear_color` now expects you to return a raw float array ([#2666](https://github.com/emilk/egui/pull/2666)). +* The `screen_reader` feature has now been renamed `web_screen_reader` and only work on web. On other platforms, use the `accesskit` feature flag instead ([#2669](https://github.com/emilk/egui/pull/2669)). + +#### Desktop/Native: +* `eframe::run_native` now returns a `Result` ([#2433](https://github.com/emilk/egui/pull/2433)). +* Update to `winit` 0.28, adding support for mac trackpad zoom ([#2654](https://github.com/emilk/egui/pull/2654)). + +#### Web: +* Prevent ctrl-P/cmd-P from opening the print dialog ([#2598](https://github.com/emilk/egui/pull/2598)). + + +## 0.20.1 - 2022-12-11 +* Fix docs.rs build ([#2420](https://github.com/emilk/egui/pull/2420)). + + +## 0.20.0 - 2022-12-08 - AccessKit integration and `wgpu` web support +* MSRV (Minimum Supported Rust Version) is now `1.65.0` ([#2314](https://github.com/emilk/egui/pull/2314)). +* Allow empty textures with the glow renderer. + +#### Desktop/Native: +* Don't repaint when just moving window ([#1980](https://github.com/emilk/egui/pull/1980)). * Added `NativeOptions::event_loop_builder` hook for apps to change platform specific event loop options ([#1952](https://github.com/emilk/egui/pull/1952)). * Enabled deferred render state initialization to support Android ([#1952](https://github.com/emilk/egui/pull/1952)). -* Allow empty textures with the glow renderer. * Added `shader_version` to `NativeOptions` for cross compiling support on different target OpenGL | ES versions (on native `glow` renderer only) ([#1993](https://github.com/emilk/egui/pull/1993)). * Fix: app state is now saved when user presses Cmd-Q on Mac ([#2013](https://github.com/emilk/egui/pull/2013)). * Added `center` to `NativeOptions` and `monitor_size` to `WindowInfo` on desktop ([#2035](https://github.com/emilk/egui/pull/2035)). -* Web: you can access your application from JS using `AppRunner::app_mut`. See `crates/egui_demo_app/src/lib.rs`. -* Web: You can now use WebGL on top of `wgpu` by enabling the `wgpu` feature (and disabling `glow` via disabling default features) ([#2107](https://github.com/emilk/egui/pull/2107)). -* Web: Add `WebInfo::user_agent` ([#2202](https://github.com/emilk/egui/pull/2202)). +* Improve IME support ([#2046](https://github.com/emilk/egui/pull/2046)). +* Added mouse-passthrough option ([#2080](https://github.com/emilk/egui/pull/2080)). +* Added `NativeOptions::fullsize_content` option on Mac to build titlebar-less windows with floating window controls ([#2049](https://github.com/emilk/egui/pull/2049)). * Wgpu device/adapter/surface creation has now various configuration options exposed via `NativeOptions/WebOptions::wgpu_options` ([#2207](https://github.com/emilk/egui/pull/2207)). * Fix: Make sure that `native_pixels_per_point` is updated ([#2256](https://github.com/emilk/egui/pull/2256)). +* Added optional, but enabled by default, integration with [AccessKit](https://accesskit.dev/) for implementing platform accessibility APIs ([#2294](https://github.com/emilk/egui/pull/2294)). +* Fix: Less flickering on resize on Windows ([#2280](https://github.com/emilk/egui/pull/2280)). + +#### Web: +* โš ๏ธ BREAKING: `start_web` is a now `async` ([#2107](https://github.com/emilk/egui/pull/2107)). +* Web: You can now use WebGL on top of `wgpu` by enabling the `wgpu` feature (and disabling `glow` via disabling default features) ([#2107](https://github.com/emilk/egui/pull/2107)). +* Web: Add `WebInfo::user_agent` ([#2202](https://github.com/emilk/egui/pull/2202)). +* Web: you can access your application from JS using `AppRunner::app_mut`. See `crates/egui_demo_app/src/lib.rs` ([#1886](https://github.com/emilk/egui/pull/1886)). ## 0.19.0 - 2022-08-20 @@ -108,7 +135,7 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C ## 0.15.0 - 2021-10-24 -* `Frame` now provides `set_window_title` to set window title dynamically +* `Frame` now provides `set_window_title` to set window title dynamically ([#828](https://github.com/emilk/egui/pull/828)). * `Frame` now provides `set_decorations` to set whether to show window decorations. * Remove "http" feature (use https://github.com/emilk/ehttp instead!). * Added `App::persist_native_window` and `App::persist_egui_memory` to control what gets persisted. diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index c4202f19..188e6583 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "eframe" -version = "0.19.0" +version = "0.20.1" authors = ["Emil Ernerfeldt "] description = "egui framework - write GUI apps that compiles to web and/or natively" edition = "2021" -rust-version = "1.62" +rust-version = "1.65" homepage = "https://github.com/emilk/egui/tree/master/crates/eframe" license = "MIT OR Apache-2.0" readme = "README.md" @@ -15,12 +15,16 @@ include = ["../LICENSE-APACHE", "../LICENSE-MIT", "**/*.rs", "Cargo.toml"] [package.metadata.docs.rs] all-features = true +targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] [lib] [features] -default = ["default_fonts", "glow"] +default = ["accesskit", "default_fonts", "glow"] + +## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/). +accesskit = ["egui/accesskit", "egui-winit/accesskit"] ## Detect dark mode system preference using [`dark-light`](https://docs.rs/dark-light). ## @@ -32,7 +36,7 @@ dark-light = ["dep:dark-light"] default_fonts = ["egui/default_fonts"] ## Use [`glow`](https://github.com/grovesNL/glow) for painting, via [`egui_glow`](https://github.com/emilk/egui/tree/master/crates/egui_glow). -glow = ["dep:glow", "egui_glow"] +glow = ["dep:glow", "dep:egui_glow", "dep:glutin"] ## Enable saving app state to disk. persistence = [ @@ -49,26 +53,33 @@ persistence = [ ## `eframe` will call `puffin::GlobalProfiler::lock().new_frame()` for you puffin = ["dep:puffin", "egui_glow?/puffin", "egui-wgpu?/puffin"] -## Enable screen reader support (requires `ctx.options().screen_reader = true;`) -screen_reader = ["egui-winit/screen_reader", "tts"] +## Enable screen reader support (requires `ctx.options_mut(|o| o.screen_reader = true);`) on web. +## +## For other platforms, use the "accesskit" feature instead. +web_screen_reader = ["tts"] + +## If set, eframe will look for the env-var `EFRAME_SCREENSHOT_TO` and write a screenshot to that location, and then quit. +## This is used to generate images for the examples. +__screenshot = ["dep:image"] ## Use [`wgpu`](https://docs.rs/wgpu) for painting (via [`egui-wgpu`](https://github.com/emilk/egui/tree/master/crates/egui-wgpu)). ## This overrides the `glow` feature. -wgpu = ["dep:wgpu", "dep:egui-wgpu"] +wgpu = ["dep:wgpu", "dep:egui-wgpu", "dep:pollster"] [dependencies] -egui = { version = "0.19.0", path = "../egui", default-features = false, features = [ +egui = { version = "0.20.0", path = "../egui", default-features = false, features = [ "bytemuck", "tracing", ] } +thiserror = "1.0.37" tracing = { version = "0.1", default-features = false, features = ["std"] } #! ### Optional dependencies ## Enable this when generating docs. document-features = { version = "0.2", optional = true } -egui_glow = { version = "0.19.0", path = "../egui_glow", optional = true, default-features = false } +egui_glow = { version = "0.20.0", path = "../egui_glow", optional = true, default-features = false } glow = { version = "0.11", optional = true } ron = { version = "0.8", optional = true, features = ["integer128"] } serde = { version = "1", optional = true, features = ["derive"] } @@ -76,21 +87,36 @@ serde = { version = "1", optional = true, features = ["derive"] } # ------------------------------------------- # native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -egui-winit = { version = "0.19.0", path = "../egui-winit", default-features = false, features = [ +egui-winit = { version = "0.20.0", path = "../egui-winit", default-features = false, features = [ "clipboard", "links", ] } -glutin = { version = "0.29.0" } -winit = "0.27.2" +raw-window-handle = { version = "0.5.0" } +winit = "0.28" # optional native: -dark-light = { version = "0.2.1", optional = true } +dark-light = { version = "1.0", optional = true } directories-next = { version = "2", optional = true } -egui-wgpu = { version = "0.19.0", path = "../egui-wgpu", optional = true, features = [ +egui-wgpu = { version = "0.20.0", path = "../egui-wgpu", optional = true, features = [ "winit", +] } # if wgpu is used, use it with winit +pollster = { version = "0.2", optional = true } # needed for wgpu + +# we can expose these to user so that they can select which backends they want to enable to avoid compiling useless deps. +# this can be done at the same time we expose x11/wayland features of winit crate. +glutin = { version = "0.30.0", optional = true, es = [ + "egl", + "glx", + "x11", + "wayland", + "wgl", +] } + +image = { version = "0.24", optional = true, default-features = false, features = [ + "png", ] } puffin = { version = "0.14", optional = true } -wgpu = { version = "0.14", optional = true } +wgpu = { version = "0.15.0", optional = true } # ------------------------------------------- # web: @@ -98,7 +124,7 @@ wgpu = { version = "0.14", optional = true } bytemuck = "1.7" js-sys = "0.3" percent-encoding = "2.1" -wasm-bindgen = "0.2" +wasm-bindgen = "=0.2.83" wasm-bindgen-futures = "0.4" web-sys = { version = "0.3.58", features = [ "BinaryType", @@ -144,6 +170,6 @@ web-sys = { version = "0.3.58", features = [ ] } # optional web: -egui-wgpu = { version = "0.19.0", path = "../egui-wgpu", optional = true } # if wgpu is used, use it without (!) winit -tts = { version = "0.24", optional = true } -wgpu = { version = "0.14", optional = true, features = ["webgl"] } +egui-wgpu = { version = "0.20.0", path = "../egui-wgpu", optional = true } # if wgpu is used, use it without (!) winit +tts = { version = "0.25", optional = true, default-features = false } +wgpu = { version = "0.15.0", optional = true, features = ["webgl"] } diff --git a/crates/eframe/README.md b/crates/eframe/README.md index 609b3571..dfcfa6ef 100644 --- a/crates/eframe/README.md +++ b/crates/eframe/README.md @@ -45,7 +45,6 @@ You can also use `egui_glow` and [`winit`](https://github.com/rust-windowing/win * Mobile text editing is not as good as for a normal web app. * Accessibility: There is an experimental screen reader for `eframe`, but it has to be enabled explicitly. There is no JS function to ask "Does the user want a screen reader?" (and there should probably not be such a function, due to user tracking/integrity concerns). * No integration with browser settings for colors and fonts. -* On Linux and Mac, Firefox will copy the WebGL render target from GPU, to CPU and then back again (https://bugzilla.mozilla.org/show_bug.cgi?id=1010527#c0), slowing down egui. In many ways, `eframe` is trying to make the browser do something it wasn't designed to do (though there are many things browser vendors could do to improve how well libraries like egui work). diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index e2eb2c71..51700d2a 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -10,7 +10,7 @@ use std::any::Any; #[cfg(not(target_arch = "wasm32"))] -pub use crate::native::run::RequestRepaintEvent; +pub use crate::native::run::UserEvent; #[cfg(not(target_arch = "wasm32"))] pub use winit::event_loop::EventLoopBuilder; @@ -20,7 +20,7 @@ pub use winit::event_loop::EventLoopBuilder; /// You can configure any platform specific details required on top of the default configuration /// done by `EFrame`. #[cfg(not(target_arch = "wasm32"))] -pub type EventLoopBuilderHook = Box)>; +pub type EventLoopBuilderHook = Box)>; /// This is how your app is created. /// @@ -74,7 +74,7 @@ pub trait App { /// /// Can be used from web to interact or other external context. /// - /// You need to implement this if you want to be able to access the application from JS using [`AppRunner::app_mut`]. + /// You need to implement this if you want to be able to access the application from JS using [`crate::web::backend::AppRunner`]. /// /// This is needed because downcasting `Box` -> `Box` to get &`ConcreteApp` is not simple in current rust. /// @@ -145,20 +145,25 @@ pub trait App { /// The size limit of the web app canvas. /// /// By default the max size is [`egui::Vec2::INFINITY`], i.e. unlimited. - /// - /// A large canvas can lead to bad frame rates on some older browsers on some platforms - /// (see ). fn max_size_points(&self) -> egui::Vec2 { egui::Vec2::INFINITY } - /// Background color for the app, e.g. what is sent to `gl.clearColor`. + /// Background color values for the app, e.g. what is sent to `gl.clearColor`. + /// /// This is the background of your windows if you don't set a central panel. - fn clear_color(&self, _visuals: &egui::Visuals) -> egui::Rgba { + /// + /// ATTENTION: + /// Since these float values go to the render as-is, any color space conversion as done + /// e.g. by converting from [`egui::Color32`] to [`egui::Rgba`] may cause incorrect results. + /// egui recommends that rendering backends use a normal "gamma-space" (non-sRGB-aware) blending, + /// which means the values you return here should also be in `sRGB` gamma-space in the 0-1 range. + /// You can use [`egui::Color32::to_normalized_gamma_f32`] for this. + fn clear_color(&self, _visuals: &egui::Visuals) -> [f32; 4] { // NOTE: a bright gray makes the shadows of the windows look weird. // We use a bit of transparency so that if the user switches on the // `transparent()` option they get immediate results. - egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).into() + egui::Color32::from_rgba_unmultiplied(12, 12, 12, 180).to_normalized_gamma_f32() // _visuals.window_fill() would also be a natural choice } @@ -176,7 +181,7 @@ pub trait App { } /// If `true` a warm-up call to [`Self::update`] will be issued where - /// `ctx.memory().everything_is_visible()` will be set to `true`. + /// `ctx.memory(|mem| mem.everything_is_visible())` will be set to `true`. /// /// This can help pre-caching resources loaded by different parts of the UI, preventing stutter later on. /// @@ -258,10 +263,10 @@ pub struct NativeOptions { /// The initial inner size of the native window in points (logical pixels). pub initial_window_size: Option, - /// The minimum inner window size + /// The minimum inner window size in points (logical pixels). pub min_window_size: Option, - /// The maximum inner window size + /// The maximum inner window size in points (logical pixels). pub max_window_size: Option, /// Should the app window be resizable? @@ -432,6 +437,7 @@ impl NativeOptions { match dark_light::detect() { dark_light::Mode::Dark => Some(Theme::Dark), dark_light::Mode::Light => Some(Theme::Light), + dark_light::Mode::Default => None, } } else { None @@ -708,6 +714,7 @@ impl Frame { #[doc(alias = "exit")] #[doc(alias = "quit")] pub fn close(&mut self) { + tracing::debug!("eframe::Frame::close called"); self.output.close = true; } @@ -734,6 +741,7 @@ impl Frame { #[cfg(not(target_arch = "wasm32"))] pub fn set_window_size(&mut self, size: egui::Vec2) { self.output.window_size = Some(size); + self.info.window_info.size = size; // so that subsequent calls see the updated value } /// Set the desired title of the window. @@ -754,12 +762,14 @@ impl Frame { #[cfg(not(target_arch = "wasm32"))] pub fn set_fullscreen(&mut self, fullscreen: bool) { self.output.fullscreen = Some(fullscreen); + self.info.window_info.fullscreen = fullscreen; // so that subsequent calls see the updated value } /// set the position of the outer window. #[cfg(not(target_arch = "wasm32"))] pub fn set_window_pos(&mut self, pos: egui::Pos2) { self.output.window_pos = Some(pos); + self.info.window_info.position = Some(pos); // so that subsequent calls see the updated value } /// When called, the native window will follow the diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index b8e2ba2e..4c4da674 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -103,9 +103,20 @@ pub use web_sys; /// /// You can add more callbacks like this if you want to call in to your code. /// #[cfg(target_arch = "wasm32")] /// #[wasm_bindgen] -/// pub async fn start(canvas_id: &str) -> Result, eframe::wasm_bindgen::JsValue> { +/// pub struct WebHandle { +/// handle: AppRunnerRef, +/// } +/// #[cfg(target_arch = "wasm32")] +/// #[wasm_bindgen] +/// pub async fn start(canvas_id: &str) -> Result { /// let web_options = eframe::WebOptions::default(); -/// eframe::start_web(canvas_id, web_options, Box::new(|cc| Box::new(MyEguiApp::new(cc)))).await +/// eframe::start_web( +/// canvas_id, +/// web_options, +/// Box::new(|cc| Box::new(MyEguiApp::new(cc))), +/// ) +/// .await +/// .map(|handle| WebHandle { handle }) /// } /// ``` /// @@ -116,7 +127,7 @@ pub async fn start_web( canvas_id: &str, web_options: WebOptions, app_creator: AppCreator, -) -> Result { +) -> std::result::Result { let handle = web::start(canvas_id, web_options, app_creator).await?; Ok(handle) @@ -163,26 +174,63 @@ mod native; /// } /// } /// ``` +/// +/// # Errors +/// This function can fail if we fail to set up a graphics context. #[cfg(not(target_arch = "wasm32"))] #[allow(clippy::needless_pass_by_value)] -pub fn run_native(app_name: &str, native_options: NativeOptions, app_creator: AppCreator) { +pub fn run_native( + app_name: &str, + native_options: NativeOptions, + app_creator: AppCreator, +) -> Result<()> { let renderer = native_options.renderer; + #[cfg(not(feature = "__screenshot"))] + assert!( + std::env::var("EFRAME_SCREENSHOT_TO").is_err(), + "EFRAME_SCREENSHOT_TO found without compiling with the '__screenshot' feature" + ); + match renderer { #[cfg(feature = "glow")] Renderer::Glow => { tracing::debug!("Using the glow renderer"); - native::run::run_glow(app_name, native_options, app_creator); + native::run::run_glow(app_name, native_options, app_creator) } #[cfg(feature = "wgpu")] Renderer::Wgpu => { tracing::debug!("Using the wgpu renderer"); - native::run::run_wgpu(app_name, native_options, app_creator); + native::run::run_wgpu(app_name, native_options, app_creator) } } } +// ---------------------------------------------------------------------------- + +/// The different problems that can occur when trying to run `eframe`. +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[cfg(not(target_arch = "wasm32"))] + #[error("winit error: {0}")] + Winit(#[from] winit::error::OsError), + + #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] + #[error("glutin error: {0}")] + Glutin(#[from] glutin::error::Error), + + #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] + #[error("Found no glutin configs matching the template: {0:?}")] + NoGlutinConfigs(glutin::config::ConfigTemplate), + + #[cfg(feature = "wgpu")] + #[error("WGPU error: {0}")] + Wgpu(#[from] egui_wgpu::WgpuError), +} + +pub type Result = std::result::Result; + // --------------------------------------------------------------------------- /// Profiling macro for feature "puffin" diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 88d4c17c..faa26537 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -3,6 +3,11 @@ use winit::event_loop::EventLoopWindowTarget; #[cfg(target_os = "macos")] use winit::platform::macos::WindowBuilderExtMacOS as _; +#[cfg(feature = "accesskit")] +use egui::accesskit; +use egui::NumExt as _; +#[cfg(feature = "accesskit")] +use egui_winit::accesskit_winit; use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings}; use crate::{epi, Theme, WindowInfo}; @@ -44,10 +49,12 @@ pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) - } } -pub fn window_builder( +pub fn build_window( + event_loop: &EventLoopWindowTarget, + title: &str, native_options: &epi::NativeOptions, - window_settings: &Option, -) -> winit::window::WindowBuilder { + window_settings: Option, +) -> Result { let epi::NativeOptions { always_on_top, maximized, @@ -63,19 +70,23 @@ pub fn window_builder( max_window_size, resizable, transparent, + centered, .. } = native_options; let window_icon = icon_data.clone().and_then(load_icon); let mut window_builder = winit::window::WindowBuilder::new() - .with_always_on_top(*always_on_top) + .with_title(title) .with_decorations(*decorated) .with_fullscreen(fullscreen.then(|| winit::window::Fullscreen::Borderless(None))) .with_maximized(*maximized) .with_resizable(*resizable) .with_transparent(*transparent) - .with_window_icon(window_icon); + .with_window_icon(window_icon) + // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 + // We must also keep the window hidden until AccessKit is initialized. + .with_visible(false); #[cfg(target_os = "macos")] if *fullsize_content { @@ -94,21 +105,69 @@ pub fn window_builder( window_builder = window_builder_drag_and_drop(window_builder, *drag_and_drop_support); - if let Some(window_settings) = window_settings { + let inner_size_points = if let Some(mut window_settings) = window_settings { + // Restore pos/size from previous session + window_settings.clamp_to_sane_values(largest_monitor_point_size(event_loop)); window_builder = window_settings.initialize_window(window_builder); + window_settings.inner_size_points() } else { if let Some(pos) = *initial_window_pos { - window_builder = window_builder.with_position(winit::dpi::PhysicalPosition { + window_builder = window_builder.with_position(winit::dpi::LogicalPosition { x: pos.x as f64, y: pos.y as f64, }); } + if let Some(initial_window_size) = *initial_window_size { + let initial_window_size = + initial_window_size.at_most(largest_monitor_point_size(event_loop)); window_builder = window_builder.with_inner_size(points_to_size(initial_window_size)); } + + *initial_window_size + }; + + if *centered { + if let Some(monitor) = event_loop.available_monitors().next() { + let monitor_size = monitor.size(); + let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 }); + if monitor_size.width > 0 && monitor_size.height > 0 { + let x = (monitor_size.width - inner_size.x as u32) / 2; + let y = (monitor_size.height - inner_size.y as u32) / 2; + window_builder = window_builder.with_position(winit::dpi::LogicalPosition { + x: x as f64, + y: y as f64, + }); + } + } } - window_builder + let window = window_builder.build(event_loop)?; + + use winit::window::WindowLevel; + window.set_window_level(if *always_on_top { + WindowLevel::AlwaysOnTop + } else { + WindowLevel::Normal + }); + + Ok(window) +} + +fn largest_monitor_point_size(event_loop: &EventLoopWindowTarget) -> egui::Vec2 { + let mut max_size = egui::Vec2::ZERO; + + for monitor in event_loop.available_monitors() { + let size = monitor.size().to_logical::(monitor.scale_factor()); + let size = egui::vec2(size.width, size.height); + max_size = max_size.max(size); + } + + if max_size == egui::Vec2::ZERO { + egui::Vec2::splat(16000.0) + } else { + max_size + } } fn load_icon(icon_data: epi::IconData) -> Option { @@ -167,7 +226,7 @@ pub fn handle_app_output( } if let Some(fullscreen) = fullscreen { - window.set_fullscreen(fullscreen.then(|| winit::window::Fullscreen::Borderless(None))); + window.set_fullscreen(fullscreen.then_some(winit::window::Fullscreen::Borderless(None))); } if let Some(window_title) = window_title { @@ -186,7 +245,12 @@ pub fn handle_app_output( } if let Some(always_on_top) = always_on_top { - window.set_always_on_top(always_on_top); + use winit::window::WindowLevel; + window.set_window_level(if always_on_top { + WindowLevel::AlwaysOnTop + } else { + WindowLevel::Normal + }); } if let Some(minimized) = minimized { @@ -235,7 +299,8 @@ impl EpiIntegration { ) -> Self { let egui_ctx = egui::Context::default(); - *egui_ctx.memory() = load_egui_memory(storage.as_deref()).unwrap_or_default(); + let memory = load_egui_memory(storage.as_deref()).unwrap_or_default(); + egui_ctx.memory_mut(|mem| *mem = memory); let native_pixels_per_point = window.scale_factor() as f32; @@ -272,13 +337,33 @@ impl EpiIntegration { } } + #[cfg(feature = "accesskit")] + pub fn init_accesskit + Send>( + &mut self, + window: &winit::window::Window, + event_loop_proxy: winit::event_loop::EventLoopProxy, + ) { + let egui_ctx = self.egui_ctx.clone(); + self.egui_winit + .init_accesskit(window, event_loop_proxy, move || { + // This function is called when an accessibility client + // (e.g. screen reader) makes its first request. If we got here, + // we know that an accessibility tree is actually wanted. + egui_ctx.enable_accesskit(); + // Enqueue a repaint so we'll receive a full tree update soon. + egui_ctx.request_repaint(); + egui::accesskit_placeholder_tree_update() + }); + } + pub fn warm_up(&mut self, app: &mut dyn epi::App, window: &winit::window::Window) { crate::profile_function!(); - let saved_memory: egui::Memory = self.egui_ctx.memory().clone(); - self.egui_ctx.memory().set_everything_is_visible(true); + let saved_memory: egui::Memory = self.egui_ctx.memory(|mem| mem.clone()); + self.egui_ctx + .memory_mut(|mem| mem.set_everything_is_visible(true)); let full_output = self.update(app, window); self.pending_full_output.append(full_output); // Handle it next frame - *self.egui_ctx.memory() = saved_memory; // We don't want to remember that windows were huge. + self.egui_ctx.memory_mut(|mem| *mem = saved_memory); // We don't want to remember that windows were huge. self.egui_ctx.clear_animations(); } @@ -295,8 +380,15 @@ impl EpiIntegration { use winit::event::{ElementState, MouseButton, WindowEvent}; match event { - WindowEvent::CloseRequested => self.close = app.on_close_event(), - WindowEvent::Destroyed => self.close = true, + WindowEvent::CloseRequested => { + tracing::debug!("Received WindowEvent::CloseRequested"); + self.close = app.on_close_event(); + tracing::debug!("App::on_close_event returned {}", self.close); + } + WindowEvent::Destroyed => { + tracing::debug!("Received WindowEvent::Destroyed"); + self.close = true; + } WindowEvent::MouseInput { button: MouseButton::Left, state: ElementState::Pressed, @@ -311,6 +403,11 @@ impl EpiIntegration { self.egui_winit.on_event(&self.egui_ctx, event) } + #[cfg(feature = "accesskit")] + pub fn on_accesskit_action_request(&mut self, request: accesskit::ActionRequest) { + self.egui_winit.on_accesskit_action_request(request); + } + pub fn update( &mut self, app: &mut dyn epi::App, @@ -333,12 +430,13 @@ impl EpiIntegration { self.can_drag_window = false; if app_output.close { self.close = app.on_close_event(); + tracing::debug!("App::on_close_event returned {}", self.close); } self.frame.output.visible = app_output.visible; // this is handled by post_present handle_app_output(window, self.egui_ctx.pixels_per_point(), app_output); } - let frame_time = (std::time::Instant::now() - frame_start).as_secs_f64() as f32; + let frame_time = frame_start.elapsed().as_secs_f64() as f32; self.frame.info.cpu_usage = Some(frame_time); full_output @@ -392,7 +490,8 @@ impl EpiIntegration { } if _app.persist_egui_memory() { crate::profile_scope!("egui_memory"); - epi::set_value(storage, STORAGE_EGUI_MEMORY_KEY, &*self.egui_ctx.memory()); + self.egui_ctx + .memory(|mem| epi::set_value(storage, STORAGE_EGUI_MEMORY_KEY, mem)); } { crate::profile_scope!("App::save"); diff --git a/crates/eframe/src/native/file_storage.rs b/crates/eframe/src/native/file_storage.rs index 29e86abf..331f40fe 100644 --- a/crates/eframe/src/native/file_storage.rs +++ b/crates/eframe/src/native/file_storage.rs @@ -26,6 +26,7 @@ impl FileStorage { /// Store the state in this .ron file. pub fn from_ron_filepath(ron_filepath: impl Into) -> Self { let ron_filepath: PathBuf = ron_filepath.into(); + tracing::debug!("Loading app state from {:?}โ€ฆ", ron_filepath); Self { kv: read_ron(&ron_filepath).unwrap_or_default(), ron_filepath, diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index fb1e5125..8f7b8d0c 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -1,19 +1,35 @@ //! Note that this file contains two similar paths - one for [`glow`], one for [`wgpu`]. //! When making changes to one you often also want to apply it to the other. -use std::time::Duration; -use std::time::Instant; +use std::time::{Duration, Instant}; -use egui_winit::winit; use winit::event_loop::{ ControlFlow, EventLoop, EventLoopBuilder, EventLoopProxy, EventLoopWindowTarget, }; +#[cfg(feature = "accesskit")] +use egui_winit::accesskit_winit; +use egui_winit::winit; + +use crate::{epi, Result}; + use super::epi_integration::{self, EpiIntegration}; -use crate::epi; + +// ---------------------------------------------------------------------------- #[derive(Debug)] -pub struct RequestRepaintEvent; +pub enum UserEvent { + RequestRepaint, + #[cfg(feature = "accesskit")] + AccessKitActionRequest(accesskit_winit::ActionRequestEvent), +} + +#[cfg(feature = "accesskit")] +impl From for UserEvent { + fn from(inner: accesskit_winit::ActionRequestEvent) -> Self { + Self::AccessKitActionRequest(inner) + } +} // ---------------------------------------------------------------------------- @@ -45,14 +61,14 @@ trait WinitApp { fn paint(&mut self) -> EventResult; fn on_event( &mut self, - event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, RequestRepaintEvent>, - ) -> EventResult; + event_loop: &EventLoopWindowTarget, + event: &winit::event::Event<'_, UserEvent>, + ) -> Result; } fn create_event_loop_builder( native_options: &mut epi::NativeOptions, -) -> EventLoopBuilder { +) -> EventLoopBuilder { let mut event_loop_builder = winit::event_loop::EventLoopBuilder::with_user_event(); if let Some(hook) = std::mem::take(&mut native_options.event_loop_builder) { @@ -66,12 +82,12 @@ fn create_event_loop_builder( /// /// We reuse the event-loop so we can support closing and opening an eframe window /// multiple times. This is just a limitation of winit. -fn with_event_loop( +fn with_event_loop( mut native_options: epi::NativeOptions, - f: impl FnOnce(&mut EventLoop, NativeOptions), -) { + f: impl FnOnce(&mut EventLoop, NativeOptions) -> R, +) -> R { use std::cell::RefCell; - thread_local!(static EVENT_LOOP: RefCell>> = RefCell::new(None)); + thread_local!(static EVENT_LOOP: RefCell>> = RefCell::new(None)); EVENT_LOOP.with(|event_loop| { // Since we want to reference NativeOptions when creating the EventLoop we can't @@ -80,22 +96,31 @@ fn with_event_loop( let mut event_loop = event_loop.borrow_mut(); let event_loop = event_loop .get_or_insert_with(|| create_event_loop_builder(&mut native_options).build()); - f(event_loop, native_options); - }); + f(event_loop, native_options) + }) } -fn run_and_return(event_loop: &mut EventLoop, mut winit_app: impl WinitApp) { +fn run_and_return( + event_loop: &mut EventLoop, + mut winit_app: impl WinitApp, +) -> Result<()> { use winit::platform::run_return::EventLoopExtRunReturn as _; - tracing::debug!("event_loop.run_return"); + tracing::debug!("Entering the winit event loop (run_return)โ€ฆ"); let mut next_repaint_time = Instant::now(); + let mut returned_result = Ok(()); + event_loop.run_return(|event, event_loop, control_flow| { let event_result = match &event { winit::event::Event::LoopDestroyed => { - tracing::debug!("winit::event::Event::LoopDestroyed"); - EventResult::Exit + // On Mac, Cmd-Q we get here and then `run_return` doesn't return (despite its name), + // so we need to save state now: + tracing::debug!("Received Event::LoopDestroyed - saving app stateโ€ฆ"); + winit_app.save_and_destroy(); + *control_flow = ControlFlow::Exit; + return; } // Platform-dependent event handlers to workaround a winit bug @@ -110,7 +135,7 @@ fn run_and_return(event_loop: &mut EventLoop, mut winit_app winit_app.paint() } - winit::event::Event::UserEvent(RequestRepaintEvent) + winit::event::Event::UserEvent(UserEvent::RequestRepaint) | winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached { .. }) => EventResult::RepaintNext, @@ -124,15 +149,28 @@ fn run_and_return(event_loop: &mut EventLoop, mut winit_app EventResult::Wait } - event => winit_app.on_event(event_loop, event), + event => match winit_app.on_event(event_loop, event) { + Ok(event_result) => event_result, + Err(err) => { + returned_result = Err(err); + tracing::debug!("Exiting because of an error"); + EventResult::Exit + } + }, }; match event_result { EventResult::Wait => {} EventResult::RepaintNow => { tracing::trace!("Repaint caused by winit::Event: {:?}", event); - next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000); - winit_app.paint(); + if cfg!(windows) { + // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 + next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000); + winit_app.paint(); + } else { + // Fix for https://github.com/emilk/egui/issues/2425 + next_repaint_time = Instant::now(); + } } EventResult::RepaintNext => { tracing::trace!("Repaint caused by winit::Event: {:?}", event); @@ -142,10 +180,7 @@ fn run_and_return(event_loop: &mut EventLoop, mut winit_app next_repaint_time = next_repaint_time.min(repaint_time); } EventResult::Exit => { - // On Cmd-Q we get here and then `run_return` doesn't return, - // so we need to save state now: - tracing::debug!("Exiting event loop - saving app stateโ€ฆ"); - winit_app.save_and_destroy(); + tracing::debug!("Asking to exit event loopโ€ฆ"); *control_flow = ControlFlow::Exit; return; } @@ -174,19 +209,21 @@ fn run_and_return(event_loop: &mut EventLoop, mut winit_app event_loop.run_return(|_, _, control_flow| { control_flow.set_exit(); }); + + returned_result } -fn run_and_exit( - event_loop: EventLoop, - mut winit_app: impl WinitApp + 'static, -) -> ! { - tracing::debug!("event_loop.run"); +fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + 'static) -> ! { + tracing::debug!("Entering the winit event loop (run)โ€ฆ"); let mut next_repaint_time = Instant::now(); event_loop.run(move |event, event_loop, control_flow| { let event_result = match event { - winit::event::Event::LoopDestroyed => EventResult::Exit, + winit::event::Event::LoopDestroyed => { + tracing::debug!("Received Event::LoopDestroyed"); + EventResult::Exit + } // Platform-dependent event handlers to workaround a winit bug // See: https://github.com/rust-windowing/winit/issues/987 @@ -200,19 +237,30 @@ fn run_and_exit( winit_app.paint() } - winit::event::Event::UserEvent(RequestRepaintEvent) + winit::event::Event::UserEvent(UserEvent::RequestRepaint) | winit::event::Event::NewEvents(winit::event::StartCause::ResumeTimeReached { .. }) => EventResult::RepaintNext, - event => winit_app.on_event(event_loop, &event), + event => match winit_app.on_event(event_loop, &event) { + Ok(event_result) => event_result, + Err(err) => { + panic!("eframe encountered a fatal error: {err}"); + } + }, }; match event_result { EventResult::Wait => {} EventResult::RepaintNow => { - next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000); - winit_app.paint(); + if cfg!(windows) { + // Fix flickering on Windows, see https://github.com/emilk/egui/pull/2280 + next_repaint_time = Instant::now() + Duration::from_secs(1_000_000_000); + winit_app.paint(); + } else { + // Fix for https://github.com/emilk/egui/issues/2425 + next_repaint_time = Instant::now(); + } } EventResult::RepaintNext => { next_repaint_time = Instant::now(); @@ -221,7 +269,7 @@ fn run_and_exit( next_repaint_time = next_repaint_time.min(repaint_time); } EventResult::Exit => { - tracing::debug!("Quittingโ€ฆ"); + tracing::debug!("Quitting - saving app stateโ€ฆ"); winit_app.save_and_destroy(); #[allow(clippy::exit)] std::process::exit(0); @@ -242,33 +290,14 @@ fn run_and_exit( }) } -fn centere_window_pos( - monitor: Option, - native_options: &mut epi::NativeOptions, -) { - // Get the current_monitor. - if let Some(monitor) = monitor { - let monitor_size = monitor.size(); - let inner_size = native_options - .initial_window_size - .unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 }); - if monitor_size.width > 0 && monitor_size.height > 0 { - let x = (monitor_size.width - inner_size.x as u32) / 2; - let y = (monitor_size.height - inner_size.y as u32) / 2; - native_options.initial_window_pos = Some(egui::Pos2 { - x: x as _, - y: y as _, - }); - } - } -} - // ---------------------------------------------------------------------------- /// Run an egui app #[cfg(feature = "glow")] mod glow_integration { use std::sync::Arc; + use egui::NumExt as _; + use super::*; // Note: that the current Glutin API design tightly couples the GL context with @@ -295,11 +324,134 @@ mod glow_integration { // Conceptually this will be split out eventually so that the rest of the state // can be persistent. - gl_window: glutin::WindowedContext, + gl_window: GlutinWindowContext, + } + struct GlutinWindowContext { + window: winit::window::Window, + gl_context: glutin::context::PossiblyCurrentContext, + gl_display: glutin::display::Display, + gl_surface: glutin::surface::Surface, + } + + impl GlutinWindowContext { + // refactor this function to use `glutin-winit` crate eventually. + // preferably add android support at the same time. + #[allow(unsafe_code)] + unsafe fn new( + winit_window: winit::window::Window, + native_options: &epi::NativeOptions, + ) -> Result { + use glutin::prelude::*; + use raw_window_handle::*; + let hardware_acceleration = match native_options.hardware_acceleration { + crate::HardwareAcceleration::Required => Some(true), + crate::HardwareAcceleration::Preferred => None, + crate::HardwareAcceleration::Off => Some(false), + }; + + let raw_display_handle = winit_window.raw_display_handle(); + let raw_window_handle = winit_window.raw_window_handle(); + + // EGL is crossplatform and the official khronos way + // but sometimes platforms/drivers may not have it, so we use back up options where possible. + // TODO: check whether we can expose these options as "features", so that users can select the relevant backend they want. + + // try egl and fallback to windows wgl. Windows is the only platform that *requires* window handle to create display. + #[cfg(target_os = "windows")] + let preference = + glutin::display::DisplayApiPreference::EglThenWgl(Some(raw_window_handle)); + // try egl and fallback to x11 glx + #[cfg(target_os = "linux")] + let preference = glutin::display::DisplayApiPreference::EglThenGlx(Box::new( + winit::platform::x11::register_xlib_error_hook, + )); + #[cfg(target_os = "macos")] + let preference = glutin::display::DisplayApiPreference::Cgl; + #[cfg(target_os = "android")] + let preference = glutin::display::DisplayApiPreference::Egl; + + let gl_display = glutin::display::Display::new(raw_display_handle, preference)?; + let swap_interval = if native_options.vsync { + glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap()) + } else { + glutin::surface::SwapInterval::DontWait + }; + + let config_template = glutin::config::ConfigTemplateBuilder::new() + .prefer_hardware_accelerated(hardware_acceleration) + .with_depth_size(native_options.depth_buffer); + // we don't know if multi sampling option is set. so, check if its more than 0. + let config_template = if native_options.multisampling > 0 { + config_template.with_multisampling( + native_options + .multisampling + .try_into() + .expect("failed to fit multisamples into u8"), + ) + } else { + config_template + }; + let config_template = config_template + .with_stencil_size(native_options.stencil_buffer) + .with_transparency(native_options.transparent) + .compatible_with_native_window(raw_window_handle) + .build(); + // finds all valid configurations supported by this display that match the config_template + // this is where we will try to get a "fallback" config if we are okay with ignoring some native + // options required by user like multi sampling, srgb, transparency etc.. + // TODO: need to figure out a good fallback config template + let config = gl_display + .find_configs(config_template.clone())? + .next() + .ok_or(crate::Error::NoGlutinConfigs(config_template))?; + + let context_attributes = + glutin::context::ContextAttributesBuilder::new().build(Some(raw_window_handle)); + // for surface creation. + let (width, height): (u32, u32) = winit_window.inner_size().into(); + let width = std::num::NonZeroU32::new(width.at_least(1)).unwrap(); + let height = std::num::NonZeroU32::new(height.at_least(1)).unwrap(); + let surface_attributes = + glutin::surface::SurfaceAttributesBuilder::::new() + .build(raw_window_handle, width, height); + // start creating the gl objects + let gl_context = gl_display.create_context(&config, &context_attributes)?; + + let gl_surface = gl_display.create_window_surface(&config, &surface_attributes)?; + let gl_context = gl_context.make_current(&gl_surface)?; + gl_surface.set_swap_interval(&gl_context, swap_interval)?; + Ok(GlutinWindowContext { + window: winit_window, + gl_context, + gl_display, + gl_surface, + }) + } + + fn window(&self) -> &winit::window::Window { + &self.window + } + + fn resize(&self, physical_size: winit::dpi::PhysicalSize) { + use glutin::surface::GlSurface; + let width = std::num::NonZeroU32::new(physical_size.width.at_least(1)).unwrap(); + let height = std::num::NonZeroU32::new(physical_size.height.at_least(1)).unwrap(); + self.gl_surface.resize(&self.gl_context, width, height); + } + + fn swap_buffers(&self) -> glutin::error::Result<()> { + use glutin::surface::GlSurface; + self.gl_surface.swap_buffers(&self.gl_context) + } + + fn get_proc_address(&self, addr: &std::ffi::CStr) -> *const std::ffi::c_void { + use glutin::display::GlDisplay; + self.gl_display.get_proc_address(addr) + } } struct GlowWinitApp { - repaint_proxy: Arc>>, + repaint_proxy: Arc>>, app_name: String, native_options: epi::NativeOptions, running: Option, @@ -309,11 +461,13 @@ mod glow_integration { // suspends and resumes. app_creator: Option, is_focused: bool, + + frame_nr: u64, } impl GlowWinitApp { fn new( - event_loop: &EventLoop, + event_loop: &EventLoop, app_name: &str, native_options: epi::NativeOptions, app_creator: epi::AppCreator, @@ -325,54 +479,39 @@ mod glow_integration { running: None, app_creator: Some(app_creator), is_focused: true, + frame_nr: 0, } } #[allow(unsafe_code)] fn create_glutin_windowed_context( - event_loop: &EventLoopWindowTarget, + event_loop: &EventLoopWindowTarget, storage: Option<&dyn epi::Storage>, - title: &String, + title: &str, native_options: &NativeOptions, - ) -> ( - glutin::WindowedContext, - glow::Context, - ) { + ) -> Result<(GlutinWindowContext, glow::Context)> { crate::profile_function!(); - use crate::HardwareAcceleration; - - let hardware_acceleration = match native_options.hardware_acceleration { - HardwareAcceleration::Required => Some(true), - HardwareAcceleration::Preferred => None, - HardwareAcceleration::Off => Some(false), - }; let window_settings = epi_integration::load_window_settings(storage); - let window_builder = epi_integration::window_builder(native_options, &window_settings) - .with_title(title) - .with_visible(false); // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 + let winit_window = + epi_integration::build_window(event_loop, title, native_options, window_settings)?; + // a lot of the code below has been lifted from glutin example in their repo. + let glutin_window_context = + unsafe { GlutinWindowContext::new(winit_window, native_options)? }; + let gl = unsafe { + glow::Context::from_loader_function(|s| { + let s = std::ffi::CString::new(s) + .expect("failed to construct C string from string for gl proc address"); - let gl_window = unsafe { - glutin::ContextBuilder::new() - .with_hardware_acceleration(hardware_acceleration) - .with_depth_buffer(native_options.depth_buffer) - .with_multisampling(native_options.multisampling) - .with_stencil_buffer(native_options.stencil_buffer) - .with_vsync(native_options.vsync) - .build_windowed(window_builder, event_loop) - .unwrap() - .make_current() - .unwrap() + glutin_window_context.get_proc_address(&s) + }) }; - let gl = - unsafe { glow::Context::from_loader_function(|s| gl_window.get_proc_address(s)) }; - - (gl_window, gl) + Ok((glutin_window_context, gl)) } - fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget) { + fn init_run_state(&mut self, event_loop: &EventLoopWindowTarget) -> Result<()> { let storage = epi_integration::create_storage(&self.app_name); let (gl_window, gl) = Self::create_glutin_windowed_context( @@ -380,7 +519,7 @@ mod glow_integration { storage.as_deref(), &self.app_name, &self.native_options, - ); + )?; let gl = Arc::new(gl); let painter = @@ -398,6 +537,10 @@ mod glow_integration { #[cfg(feature = "wgpu")] None, ); + #[cfg(feature = "accesskit")] + { + integration.init_accesskit(gl_window.window(), self.repaint_proxy.lock().clone()); + } let theme = system_theme.unwrap_or(self.native_options.default_theme); integration.egui_ctx.set_visuals(theme.egui_visuals()); @@ -409,7 +552,10 @@ mod glow_integration { { let event_loop_proxy = self.repaint_proxy.clone(); integration.egui_ctx.set_request_repaint_callback(move || { - event_loop_proxy.lock().send_event(RequestRepaintEvent).ok(); + event_loop_proxy + .lock() + .send_event(UserEvent::RequestRepaint) + .ok(); }); } @@ -435,6 +581,8 @@ mod glow_integration { integration, app, }); + + Ok(()) } } @@ -515,6 +663,26 @@ mod glow_integration { integration.post_present(window); + #[cfg(feature = "__screenshot")] + // give it time to settle: + if self.frame_nr == 2 { + if let Ok(path) = std::env::var("EFRAME_SCREENSHOT_TO") { + assert!( + path.ends_with(".png"), + "Expected EFRAME_SCREENSHOT_TO to end with '.png', got {path:?}" + ); + let [w, h] = screen_size_in_pixels; + let pixels = painter.read_screen_rgba(screen_size_in_pixels); + let image = image::RgbaImage::from_vec(w, h, pixels).unwrap(); + let image = image::imageops::flip_vertical(&image); + image.save(&path).unwrap_or_else(|err| { + panic!("Failed to save screenshot to {path:?}: {err}"); + }); + eprintln!("Screenshot saved to {path:?}."); + std::process::exit(0); + } + } + let control_flow = if integration.should_close() { EventResult::Exit } else if repaint_after.is_zero() { @@ -544,6 +712,8 @@ mod glow_integration { std::thread::sleep(std::time::Duration::from_millis(10)); } + self.frame_nr += 1; + control_flow } else { EventResult::Wait @@ -552,13 +722,13 @@ mod glow_integration { fn on_event( &mut self, - event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, RequestRepaintEvent>, - ) -> EventResult { - match event { + event_loop: &EventLoopWindowTarget, + event: &winit::event::Event<'_, UserEvent>, + ) -> Result { + Ok(match event { winit::event::Event::Resumed => { if self.running.is_none() { - self.init_run_state(event_loop); + self.init_run_state(event_loop)?; } EventResult::RepaintNow } @@ -621,7 +791,8 @@ mod glow_integration { winit::event::WindowEvent::CloseRequested if running.integration.should_close() => { - return EventResult::Exit + tracing::debug!("Received WindowEvent::CloseRequested"); + return Ok(EventResult::Exit); } _ => {} } @@ -644,8 +815,23 @@ mod glow_integration { EventResult::Wait } } + #[cfg(feature = "accesskit")] + winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest( + accesskit_winit::ActionRequestEvent { request, .. }, + )) => { + if let Some(running) = &mut self.running { + running + .integration + .on_accesskit_action_request(request.clone()); + // As a form of user input, accessibility actions should + // lead to a repaint. + EventResult::RepaintNext + } else { + EventResult::Wait + } + } _ => EventResult::Wait, - } + }) } } @@ -653,24 +839,15 @@ mod glow_integration { app_name: &str, mut native_options: epi::NativeOptions, app_creator: epi::AppCreator, - ) { + ) -> Result<()> { if native_options.run_and_return { - with_event_loop(native_options, |event_loop, mut native_options| { - if native_options.centered { - centere_window_pos(event_loop.available_monitors().next(), &mut native_options); - } - + with_event_loop(native_options, |event_loop, native_options| { let glow_eframe = GlowWinitApp::new(event_loop, app_name, native_options, app_creator); - run_and_return(event_loop, glow_eframe); - }); + run_and_return(event_loop, glow_eframe) + }) } else { let event_loop = create_event_loop_builder(&mut native_options).build(); - - if native_options.centered { - centere_window_pos(event_loop.available_monitors().next(), &mut native_options); - } - let glow_eframe = GlowWinitApp::new(&event_loop, app_name, native_options, app_creator); run_and_exit(event_loop, glow_eframe); } @@ -697,7 +874,7 @@ mod wgpu_integration { } struct WgpuWinitApp { - repaint_proxy: Arc>>, + repaint_proxy: Arc>>, app_name: String, native_options: epi::NativeOptions, app_creator: Option, @@ -711,11 +888,17 @@ mod wgpu_integration { impl WgpuWinitApp { fn new( - event_loop: &EventLoop, + event_loop: &EventLoop, app_name: &str, native_options: epi::NativeOptions, app_creator: epi::AppCreator, ) -> Self { + #[cfg(feature = "__screenshot")] + assert!( + std::env::var("EFRAME_SCREENSHOT_TO").is_err(), + "EFRAME_SCREENSHOT_TO not yet implemented for wgpu backend" + ); + Self { repaint_proxy: Arc::new(std::sync::Mutex::new(event_loop.create_proxy())), app_name: app_name.to_owned(), @@ -728,46 +911,47 @@ mod wgpu_integration { } fn create_window( - event_loop: &EventLoopWindowTarget, + event_loop: &EventLoopWindowTarget, storage: Option<&dyn epi::Storage>, - title: &String, + title: &str, native_options: &NativeOptions, - ) -> winit::window::Window { + ) -> std::result::Result { let window_settings = epi_integration::load_window_settings(storage); - epi_integration::window_builder(native_options, &window_settings) - .with_title(title) - .with_visible(false) // Keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279 - .build(event_loop) - .unwrap() + epi_integration::build_window(event_loop, title, native_options, window_settings) } #[allow(unsafe_code)] - fn set_window(&mut self, window: winit::window::Window) { + fn set_window( + &mut self, + window: winit::window::Window, + ) -> std::result::Result<(), egui_wgpu::WgpuError> { self.window = Some(window); if let Some(running) = &mut self.running { unsafe { - running.painter.set_window(self.window.as_ref()); + pollster::block_on(running.painter.set_window(self.window.as_ref()))?; } } + Ok(()) } #[allow(unsafe_code)] #[cfg(target_os = "android")] - fn drop_window(&mut self) { + fn drop_window(&mut self) -> std::result::Result<(), egui_wgpu::WgpuError> { self.window = None; if let Some(running) = &mut self.running { unsafe { - running.painter.set_window(None); + pollster::block_on(running.painter.set_window(None))?; } } + Ok(()) } fn init_run_state( &mut self, - event_loop: &EventLoopWindowTarget, + event_loop: &EventLoopWindowTarget, storage: Option>, window: winit::window::Window, - ) { + ) -> std::result::Result<(), egui_wgpu::WgpuError> { #[allow(unsafe_code, unused_mut, unused_unsafe)] let painter = unsafe { let mut painter = egui_wgpu::winit::Painter::new( @@ -775,7 +959,7 @@ mod wgpu_integration { self.native_options.multisampling.max(1) as _, self.native_options.depth_buffer, ); - painter.set_window(Some(&window)); + pollster::block_on(painter.set_window(Some(&window)))?; painter }; @@ -792,6 +976,10 @@ mod wgpu_integration { None, wgpu_render_state.clone(), ); + #[cfg(feature = "accesskit")] + { + integration.init_accesskit(&window, self.repaint_proxy.lock().unwrap().clone()); + } let theme = system_theme.unwrap_or(self.native_options.default_theme); integration.egui_ctx.set_visuals(theme.egui_visuals()); @@ -803,7 +991,7 @@ mod wgpu_integration { event_loop_proxy .lock() .unwrap() - .send_event(RequestRepaintEvent) + .send_event(UserEvent::RequestRepaint) .ok(); }); } @@ -829,6 +1017,8 @@ mod wgpu_integration { app, }); self.window = Some(window); + + Ok(()) } } @@ -934,10 +1124,10 @@ mod wgpu_integration { fn on_event( &mut self, - event_loop: &EventLoopWindowTarget, - event: &winit::event::Event<'_, RequestRepaintEvent>, - ) -> EventResult { - match event { + event_loop: &EventLoopWindowTarget, + event: &winit::event::Event<'_, UserEvent>, + ) -> Result { + Ok(match event { winit::event::Event::Resumed => { if let Some(running) = &self.running { if self.window.is_none() { @@ -946,8 +1136,8 @@ mod wgpu_integration { running.integration.frame.storage(), &self.app_name, &self.native_options, - ); - self.set_window(window); + )?; + self.set_window(window)?; } } else { let storage = epi_integration::create_storage(&self.app_name); @@ -956,14 +1146,14 @@ mod wgpu_integration { storage.as_deref(), &self.app_name, &self.native_options, - ); - self.init_run_state(event_loop, storage, window); + )?; + self.init_run_state(event_loop, storage, window)?; } EventResult::RepaintNow } winit::event::Event::Suspended => { #[cfg(target_os = "android")] - self.drop_window(); + self.drop_window()?; EventResult::Wait } @@ -1013,7 +1203,8 @@ mod wgpu_integration { winit::event::WindowEvent::CloseRequested if running.integration.should_close() => { - return EventResult::Exit + tracing::debug!("Received WindowEvent::CloseRequested"); + return Ok(EventResult::Exit); } _ => {} }; @@ -1035,8 +1226,23 @@ mod wgpu_integration { EventResult::Wait } } + #[cfg(feature = "accesskit")] + winit::event::Event::UserEvent(UserEvent::AccessKitActionRequest( + accesskit_winit::ActionRequestEvent { request, .. }, + )) => { + if let Some(running) = &mut self.running { + running + .integration + .on_accesskit_action_request(request.clone()); + // As a form of user input, accessibility actions should + // lead to a repaint. + EventResult::RepaintNext + } else { + EventResult::Wait + } + } _ => EventResult::Wait, - } + }) } } @@ -1044,24 +1250,15 @@ mod wgpu_integration { app_name: &str, mut native_options: epi::NativeOptions, app_creator: epi::AppCreator, - ) { + ) -> Result<()> { if native_options.run_and_return { - with_event_loop(native_options, |event_loop, mut native_options| { - if native_options.centered { - centere_window_pos(event_loop.available_monitors().next(), &mut native_options); - } - + with_event_loop(native_options, |event_loop, native_options| { let wgpu_eframe = WgpuWinitApp::new(event_loop, app_name, native_options, app_creator); - run_and_return(event_loop, wgpu_eframe); - }); + run_and_return(event_loop, wgpu_eframe) + }) } else { let event_loop = create_event_loop_builder(&mut native_options).build(); - - if native_options.centered { - centere_window_pos(event_loop.available_monitors().next(), &mut native_options); - } - let wgpu_eframe = WgpuWinitApp::new(&event_loop, app_name, native_options, app_creator); run_and_exit(event_loop, wgpu_eframe); } diff --git a/crates/eframe/src/web/backend.rs b/crates/eframe/src/web/backend.rs index c4fcaf20..107a8ce4 100644 --- a/crates/eframe/src/web/backend.rs +++ b/crates/eframe/src/web/backend.rs @@ -1,11 +1,11 @@ -use super::{web_painter::WebPainter, *}; -use crate::epi; - use egui::{ mutex::{Mutex, MutexGuard}, TexturesDelta, }; -pub use egui::{pos2, Color32}; + +use crate::{epi, App}; + +use super::{web_painter::WebPainter, *}; // ---------------------------------------------------------------------------- @@ -284,7 +284,7 @@ impl AppRunner { /// Get mutable access to the concrete [`App`] we enclose. /// /// This will panic if your app does not implement [`App::as_any_mut`]. - pub fn app_mut(&mut self) -> &mut ConreteApp { + pub fn app_mut(&mut self) -> &mut ConreteApp { self.app .as_any_mut() .expect("Your app must implement `as_any_mut`, but it doesn't") @@ -313,10 +313,11 @@ impl AppRunner { pub fn warm_up(&mut self) -> Result<(), JsValue> { if self.app.warm_up_enabled() { - let saved_memory: egui::Memory = self.egui_ctx.memory().clone(); - self.egui_ctx.memory().set_everything_is_visible(true); + let saved_memory: egui::Memory = self.egui_ctx.memory(|m| m.clone()); + self.egui_ctx + .memory_mut(|m| m.set_everything_is_visible(true)); self.logic()?; - *self.egui_ctx.memory() = saved_memory; // We don't want to remember that windows were huge. + self.egui_ctx.memory_mut(|m| *m = saved_memory); // We don't want to remember that windows were huge. self.egui_ctx.clear_animations(); } Ok(()) @@ -388,7 +389,7 @@ impl AppRunner { } fn handle_platform_output(&mut self, platform_output: egui::PlatformOutput) { - if self.egui_ctx.options().screen_reader { + if self.egui_ctx.options(|o| o.screen_reader) { self.screen_reader .speak(&platform_output.events_description()); } @@ -400,6 +401,8 @@ impl AppRunner { events: _, // already handled mutable_text_under_cursor, text_cursor_pos, + #[cfg(feature = "accesskit")] + accesskit_update: _, // not currently implemented } = platform_output; set_cursor_icon(cursor_icon); diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index b3030e0d..b8471be9 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -1,6 +1,9 @@ -use super::*; use std::sync::atomic::{AtomicBool, Ordering}; +use egui::Key; + +use super::*; + struct IsDestroyed(pub bool); pub fn paint_and_schedule( @@ -64,11 +67,13 @@ pub fn install_document_events(runner_container: &mut AppRunnerContainer) -> Res runner_lock.input.raw.modifiers = modifiers; let key = event.key(); + let egui_key = translate_key(&key); - if let Some(key) = translate_key(&key) { + if let Some(key) = egui_key { runner_lock.input.raw.events.push(egui::Event::Key { key, pressed: true, + repeat: false, // egui will fill this in for us! modifiers, }); } @@ -84,10 +89,13 @@ pub fn install_document_events(runner_container: &mut AppRunnerContainer) -> Res let egui_wants_keyboard = runner_lock.egui_ctx().wants_keyboard_input(); - let prevent_default = if matches!(event.key().as_str(), "Tab") { + #[allow(clippy::if_same_then_else)] + let prevent_default = if egui_key == Some(Key::Tab) { // Always prevent moving cursor to url bar. // egui wants to use tab to move to the next text field. true + } else if egui_key == Some(Key::P) { + true // Prevent ctrl-P opening the print dialog. Users may want to use it for a command palette. } else if egui_wants_keyboard { matches!( event.key().as_str(), @@ -111,6 +119,7 @@ pub fn install_document_events(runner_container: &mut AppRunnerContainer) -> Res if prevent_default { event.prevent_default(); + // event.stop_propagation(); } }, )?; @@ -125,6 +134,7 @@ pub fn install_document_events(runner_container: &mut AppRunnerContainer) -> Res runner_lock.input.raw.events.push(egui::Event::Key { key, pressed: false, + repeat: false, modifiers, }); } @@ -196,15 +206,21 @@ pub fn install_document_events(runner_container: &mut AppRunnerContainer) -> Res pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Result<(), JsValue> { let canvas = canvas_element(runner_container.runner.lock().canvas_id()).unwrap(); - { + let prevent_default_events = [ // By default, right-clicks open a context menu. // We don't want to do that (right clicks is handled by egui): - let event_name = "contextmenu"; + "contextmenu", + // Allow users to use ctrl-p for e.g. a command palette + "afterprint", + ]; + for event_name in prevent_default_events { let closure = move |event: web_sys::MouseEvent, mut _runner_lock: egui::mutex::MutexGuard<'_, AppRunner>| { event.prevent_default(); + // event.stop_propagation(); + // tracing::debug!("Preventing event {:?}", event_name); }; runner_container.add_event_listener(&canvas, event_name, closure)?; @@ -308,7 +324,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul modifiers, }); - push_touches(&mut *runner_lock, egui::TouchPhase::Start, &event); + push_touches(&mut runner_lock, egui::TouchPhase::Start, &event); runner_lock.needs_repaint.repaint_asap(); event.stop_propagation(); event.prevent_default(); @@ -330,7 +346,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul .events .push(egui::Event::PointerMoved(pos)); - push_touches(&mut *runner_lock, egui::TouchPhase::Move, &event); + push_touches(&mut runner_lock, egui::TouchPhase::Move, &event); runner_lock.needs_repaint.repaint_asap(); event.stop_propagation(); event.prevent_default(); @@ -357,7 +373,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul // Then remove hover effect: runner_lock.input.raw.events.push(egui::Event::PointerGone); - push_touches(&mut *runner_lock, egui::TouchPhase::End, &event); + push_touches(&mut runner_lock, egui::TouchPhase::End, &event); runner_lock.needs_repaint.repaint_asap(); event.stop_propagation(); event.prevent_default(); @@ -388,7 +404,7 @@ pub fn install_canvas_events(runner_container: &mut AppRunnerContainer) -> Resul } web_sys::WheelEvent::DOM_DELTA_LINE => { #[allow(clippy::let_and_return)] - let points_per_scroll_line = 8.0; // Note that this is intentionally different from what we use in egui_glium / winit. + let points_per_scroll_line = 8.0; // Note that this is intentionally different from what we use in winit. points_per_scroll_line } _ => 1.0, // DOM_DELTA_PIXEL diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index 18b75440..2e457763 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -91,7 +91,7 @@ pub fn canvas_element(canvas_id: &str) -> Option { pub fn canvas_element_or_die(canvas_id: &str) -> web_sys::HtmlCanvasElement { canvas_element(canvas_id) - .unwrap_or_else(|| panic!("Failed to find canvas with id '{}'", canvas_id)) + .unwrap_or_else(|| panic!("Failed to find canvas with id {:?}", canvas_id)) } fn canvas_origin(canvas_id: &str) -> egui::Pos2 { diff --git a/crates/eframe/src/web/storage.rs b/crates/eframe/src/web/storage.rs index 6a3d5279..f0f3c843 100644 --- a/crates/eframe/src/web/storage.rs +++ b/crates/eframe/src/web/storage.rs @@ -15,7 +15,7 @@ pub fn load_memory(ctx: &egui::Context) { if let Some(memory_string) = local_storage_get("egui_memory_ron") { match ron::from_str(&memory_string) { Ok(memory) => { - *ctx.memory() = memory; + ctx.memory_mut(|m| *m = memory); } Err(err) => { tracing::error!("Failed to parse memory RON: {}", err); @@ -29,7 +29,7 @@ pub fn load_memory(_: &egui::Context) {} #[cfg(feature = "persistence")] pub fn save_memory(ctx: &egui::Context) { - match ron::to_string(&*ctx.memory()) { + match ctx.memory(|mem| ron::to_string(mem)) { Ok(ron) => { local_storage_set("egui_memory_ron", &ron); } diff --git a/crates/eframe/src/web/web_painter.rs b/crates/eframe/src/web/web_painter.rs index 4ced22f4..9c7631b9 100644 --- a/crates/eframe/src/web/web_painter.rs +++ b/crates/eframe/src/web/web_painter.rs @@ -1,4 +1,3 @@ -use egui::Rgba; use wasm_bindgen::JsValue; /// Renderer for a browser canvas. @@ -19,7 +18,7 @@ pub(crate) trait WebPainter { /// Update all internal textures and paint gui. fn paint_and_update_textures( &mut self, - clear_color: Rgba, + clear_color: [f32; 4], clipped_primitives: &[egui::ClippedPrimitive], pixels_per_point: f32, textures_delta: &egui::TexturesDelta, diff --git a/crates/eframe/src/web/web_painter_glow.rs b/crates/eframe/src/web/web_painter_glow.rs index 36f42692..0fe7e127 100644 --- a/crates/eframe/src/web/web_painter_glow.rs +++ b/crates/eframe/src/web/web_painter_glow.rs @@ -2,7 +2,6 @@ use wasm_bindgen::JsCast; use wasm_bindgen::JsValue; use web_sys::HtmlCanvasElement; -use egui::Rgba; use egui_glow::glow; use crate::{WebGlContextOption, WebOptions}; @@ -49,7 +48,7 @@ impl WebPainter for WebPainterGlow { fn paint_and_update_textures( &mut self, - clear_color: Rgba, + clear_color: [f32; 4], clipped_primitives: &[egui::ClippedPrimitive], pixels_per_point: f32, textures_delta: &egui::TexturesDelta, diff --git a/crates/eframe/src/web/web_painter_wgpu.rs b/crates/eframe/src/web/web_painter_wgpu.rs index b1d835e4..ab55373f 100644 --- a/crates/eframe/src/web/web_painter_wgpu.rs +++ b/crates/eframe/src/web/web_painter_wgpu.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use wasm_bindgen::JsValue; use web_sys::HtmlCanvasElement; -use egui::{mutex::RwLock, Rgba}; +use egui::mutex::RwLock; use egui_wgpu::{renderer::ScreenDescriptor, RenderState, SurfaceErrorAction}; use crate::WebOptions; @@ -18,6 +18,8 @@ pub(crate) struct WebPainterWgpu { limits: wgpu::Limits, render_state: Option, on_surface_error: Arc SurfaceErrorAction>, + depth_format: Option, + depth_texture_view: Option, } impl WebPainterWgpu { @@ -26,14 +28,46 @@ impl WebPainterWgpu { self.render_state.clone() } + pub fn generate_depth_texture_view( + &self, + render_state: &RenderState, + width_in_pixels: u32, + height_in_pixels: u32, + ) -> Option { + let device = &render_state.device; + self.depth_format.map(|depth_format| { + device + .create_texture(&wgpu::TextureDescriptor { + label: Some("egui_depth_texture"), + size: wgpu::Extent3d { + width: width_in_pixels, + height: height_in_pixels, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: depth_format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: &[depth_format], + }) + .create_view(&wgpu::TextureViewDescriptor::default()) + }) + } + #[allow(unused)] // only used if `wgpu` is the only active feature. pub async fn new(canvas_id: &str, options: &WebOptions) -> Result { tracing::debug!("Creating wgpu painter"); let canvas = super::canvas_element_or_die(canvas_id); - let instance = wgpu::Instance::new(options.wgpu_options.backends); - let surface = instance.create_surface_from_canvas(&canvas); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: options.wgpu_options.backends, + dx12_shader_compiler: Default::default(), + }); + let surface = instance + .create_surface_from_canvas(&canvas) + .map_err(|err| format!("failed to create wgpu surface: {err}"))?; let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { @@ -53,9 +87,10 @@ impl WebPainterWgpu { .map_err(|err| format!("Failed to find wgpu device: {}", err))?; let target_format = - egui_wgpu::preferred_framebuffer_format(&surface.get_supported_formats(&adapter)); + egui_wgpu::preferred_framebuffer_format(&surface.get_capabilities(&adapter).formats); - let renderer = egui_wgpu::Renderer::new(&device, target_format, None, 1); + let depth_format = options.wgpu_options.depth_format; + let renderer = egui_wgpu::Renderer::new(&device, target_format, depth_format, 1); let render_state = RenderState { device: Arc::new(device), queue: Arc::new(queue), @@ -70,6 +105,7 @@ impl WebPainterWgpu { height: 0, present_mode: options.wgpu_options.present_mode, alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![target_format], }; tracing::debug!("wgpu painter initialized."); @@ -80,6 +116,8 @@ impl WebPainterWgpu { render_state: Some(render_state), surface, surface_configuration, + depth_format, + depth_texture_view: None, limits: options.wgpu_options.device_descriptor.limits.clone(), on_surface_error: options.wgpu_options.on_surface_error.clone(), }) @@ -97,11 +135,13 @@ impl WebPainter for WebPainterWgpu { fn paint_and_update_textures( &mut self, - clear_color: Rgba, + clear_color: [f32; 4], clipped_primitives: &[egui::ClippedPrimitive], pixels_per_point: f32, textures_delta: &egui::TexturesDelta, ) -> Result<(), JsValue> { + let size_in_pixels = [self.canvas.width(), self.canvas.height()]; + let render_state = if let Some(render_state) = &self.render_state { render_state } else { @@ -110,32 +150,6 @@ impl WebPainter for WebPainterWgpu { )); }; - // Resize surface if needed - let size_in_pixels = [self.canvas.width(), self.canvas.height()]; - if size_in_pixels[0] != self.surface_configuration.width - || size_in_pixels[1] != self.surface_configuration.height - { - self.surface_configuration.width = size_in_pixels[0]; - self.surface_configuration.height = size_in_pixels[1]; - self.surface - .configure(&render_state.device, &self.surface_configuration); - } - - let frame = match self.surface.get_current_texture() { - Ok(frame) => frame, - #[allow(clippy::single_match_else)] - Err(e) => match (*self.on_surface_error)(e) { - SurfaceErrorAction::RecreateSurface => { - self.surface - .configure(&render_state.device, &self.surface_configuration); - return Ok(()); - } - SurfaceErrorAction::SkipFrame => { - return Ok(()); - } - }, - }; - let mut encoder = render_state .device @@ -169,31 +183,77 @@ impl WebPainter for WebPainterWgpu { ) }; - { - let renderer = render_state.renderer.read(); - let frame_view = frame - .texture - .create_view(&wgpu::TextureViewDescriptor::default()); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - view: &frame_view, - resolve_target: None, - ops: wgpu::Operations { - load: wgpu::LoadOp::Clear(wgpu::Color { - r: clear_color.r() as f64, - g: clear_color.g() as f64, - b: clear_color.b() as f64, - a: clear_color.a() as f64, - }), - store: true, - }, - })], - depth_stencil_attachment: None, - label: Some("egui_render"), - }); + // Resize surface if needed + let is_zero_sized_surface = size_in_pixels[0] == 0 || size_in_pixels[1] == 0; + let frame = if is_zero_sized_surface { + None + } else { + if size_in_pixels[0] != self.surface_configuration.width + || size_in_pixels[1] != self.surface_configuration.height + { + self.surface_configuration.width = size_in_pixels[0]; + self.surface_configuration.height = size_in_pixels[1]; + self.surface + .configure(&render_state.device, &self.surface_configuration); + self.depth_texture_view = self.generate_depth_texture_view( + render_state, + size_in_pixels[0], + size_in_pixels[1], + ); + } - renderer.render(&mut render_pass, clipped_primitives, &screen_descriptor); - } + let frame = match self.surface.get_current_texture() { + Ok(frame) => frame, + #[allow(clippy::single_match_else)] + Err(e) => match (*self.on_surface_error)(e) { + SurfaceErrorAction::RecreateSurface => { + self.surface + .configure(&render_state.device, &self.surface_configuration); + return Ok(()); + } + SurfaceErrorAction::SkipFrame => { + return Ok(()); + } + }, + }; + + { + let renderer = render_state.renderer.read(); + let frame_view = frame + .texture + .create_view(&wgpu::TextureViewDescriptor::default()); + let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view: &frame_view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: clear_color[0] as f64, + g: clear_color[1] as f64, + b: clear_color[2] as f64, + a: clear_color[3] as f64, + }), + store: true, + }, + })], + depth_stencil_attachment: self.depth_texture_view.as_ref().map(|view| { + wgpu::RenderPassDepthStencilAttachment { + view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: false, + }), + stencil_ops: None, + } + }), + label: Some("egui_render"), + }); + + renderer.render(&mut render_pass, clipped_primitives, &screen_descriptor); + } + + Some(frame) + }; { let mut renderer = render_state.renderer.write(); @@ -208,7 +268,10 @@ impl WebPainter for WebPainterWgpu { .into_iter() .chain(std::iter::once(encoder.finish())), ); - frame.present(); + + if let Some(frame) = frame { + frame.present(); + } Ok(()) } diff --git a/crates/egui-wgpu/CHANGELOG.md b/crates/egui-wgpu/CHANGELOG.md index c5499396..b187497b 100644 --- a/crates/egui-wgpu/CHANGELOG.md +++ b/crates/egui-wgpu/CHANGELOG.md @@ -3,16 +3,26 @@ All notable changes to the `egui-wgpu` integration will be noted in this file. ## Unreleased +* update to wgpu 0.15 ([#2629](https://github.com/emilk/egui/pull/2629)) +* Return `Err` instead of panic if we can't find a device ([#2428](https://github.com/emilk/egui/pull/2428)). +* `winit::Painter::set_window` is now `async` ([#2434](https://github.com/emilk/egui/pull/2434)). +* `egui-wgpu` now only depends on `epaint` instead of the entire `egui` ([#2438](https://github.com/emilk/egui/pull/2438)). + + +## 0.20.0 - 2022-12-08 - web support * Renamed `RenderPass` to `Renderer`. * Renamed `RenderPass::execute` to `RenderPass::render`. * Renamed `RenderPass::execute_with_renderpass` to `Renderer::render` (replacing existing `Renderer::render`) * Reexported `Renderer`. +* You can now use `egui-wgpu` on web, using WebGL ([#2107](https://github.com/emilk/egui/pull/2107)). * `Renderer` no longer handles pass creation and depth buffer creation ([#2136](https://github.com/emilk/egui/pull/2136)) * `PrepareCallback` now passes `wgpu::CommandEncoder` ([#2136](https://github.com/emilk/egui/pull/2136)) * `PrepareCallback` can now returns `wgpu::CommandBuffer` that are bundled into a single `wgpu::Queue::submit` call ([#2230](https://github.com/emilk/egui/pull/2230)) * Only a single vertex & index buffer is now created and resized when necessary (previously, vertex/index buffers were allocated for every mesh) ([#2148](https://github.com/emilk/egui/pull/2148)). * `Renderer::update_texture` no longer creates a new `wgpu::Sampler` with every new texture ([#2198](https://github.com/emilk/egui/pull/2198)) * `Painter`'s instance/device/adapter/surface creation is now configurable via `WgpuConfiguration` ([#2207](https://github.com/emilk/egui/pull/2207)) +* Fix panic on using a depth buffer ([#2316](https://github.com/emilk/egui/pull/2316)) + ## 0.19.0 - 2022-08-20 * Enables deferred render + surface state initialization for Android ([#1634](https://github.com/emilk/egui/pull/1634)). diff --git a/crates/egui-wgpu/Cargo.toml b/crates/egui-wgpu/Cargo.toml index dad6ce7a..7a0425cb 100644 --- a/crates/egui-wgpu/Cargo.toml +++ b/crates/egui-wgpu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "egui-wgpu" -version = "0.19.0" +version = "0.20.0" description = "Bindings for using egui natively using the wgpu library" authors = [ "Nils Hasenbanck ", @@ -8,7 +8,7 @@ authors = [ "Emil Ernerfeldt ", ] edition = "2021" -rust-version = "1.62" +rust-version = "1.65" homepage = "https://github.com/emilk/egui/tree/master/crates/egui-wgpu" license = "MIT OR Apache-2.0" readme = "README.md" @@ -32,25 +32,24 @@ all-features = true puffin = ["dep:puffin"] ## Enable [`winit`](https://docs.rs/winit) integration. -winit = ["dep:pollster", "dep:winit"] +winit = ["dep:winit"] [dependencies] -egui = { version = "0.19.0", path = "../egui", default-features = false, features = [ +epaint = { version = "0.20.0", path = "../epaint", default-features = false, features = [ "bytemuck", ] } bytemuck = "1.7" tracing = { version = "0.1", default-features = false, features = ["std"] } type-map = "0.5.0" -wgpu = "0.14" +wgpu = "0.15.0" #! ### Optional dependencies ## Enable this when generating docs. document-features = { version = "0.2", optional = true } -pollster = { version = "0.2", optional = true } -winit = { version = "0.27.2", optional = true } +winit = { version = "0.28", optional = true } # Native: [target.'cfg(not(target_arch = "wasm32"))'.dependencies] diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index d4381fe1..339df180 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -8,18 +8,19 @@ pub use wgpu; -/// Low-level painting of [`egui`] on [`wgpu`]. +/// Low-level painting of [`egui`](https://github.com/emilk/egui) on [`wgpu`]. pub mod renderer; pub use renderer::CallbackFn; pub use renderer::Renderer; -/// Module for painting [`egui`] with [`wgpu`] on [`winit`]. +/// Module for painting [`egui`](https://github.com/emilk/egui) with [`wgpu`] on [`winit`]. #[cfg(feature = "winit")] pub mod winit; -use egui::mutex::RwLock; use std::sync::Arc; +use epaint::mutex::RwLock; + /// Access to the render state for egui. #[derive(Clone)] pub struct RenderState { @@ -55,6 +56,8 @@ pub struct WgpuConfiguration { /// Callback for surface errors. pub on_surface_error: Arc SurfaceErrorAction>, + + pub depth_format: Option, } impl Default for WgpuConfiguration { @@ -68,6 +71,7 @@ impl Default for WgpuConfiguration { backends: wgpu::Backends::PRIMARY | wgpu::Backends::GL, present_mode: wgpu::PresentMode::AutoVsync, power_preference: wgpu::PowerPreference::HighPerformance, + depth_format: None, on_surface_error: Arc::new(|err| { if err == wgpu::SurfaceError::Outdated { @@ -95,7 +99,35 @@ pub fn preferred_framebuffer_format(formats: &[wgpu::TextureFormat]) -> wgpu::Te } formats[0] // take the first } - +// maybe use this-error? +#[derive(Debug)] +pub enum WgpuError { + DeviceError(wgpu::RequestDeviceError), + SurfaceError(wgpu::CreateSurfaceError), +} +impl std::fmt::Display for WgpuError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Debug::fmt(self, f) + } +} +impl std::error::Error for WgpuError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + match self { + WgpuError::DeviceError(e) => e.source(), + WgpuError::SurfaceError(e) => e.source(), + } + } +} +impl From for WgpuError { + fn from(e: wgpu::RequestDeviceError) -> Self { + Self::DeviceError(e) + } +} +impl From for WgpuError { + fn from(e: wgpu::CreateSurfaceError) -> Self { + Self::SurfaceError(e) + } +} // --------------------------------------------------------------------------- /// Profiling macro for feature "puffin" diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index d7112b29..524d9893 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -4,14 +4,13 @@ use std::num::NonZeroU64; use std::ops::Range; use std::{borrow::Cow, collections::HashMap, num::NonZeroU32}; -use egui::epaint::Vertex; -use egui::NumExt; -use egui::{epaint::Primitive, PaintCallbackInfo}; use type_map::concurrent::TypeMap; use wgpu; use wgpu::util::DeviceExt as _; -/// A callback function that can be used to compose an [`egui::PaintCallback`] for custom WGPU +use epaint::{emath::NumExt, PaintCallbackInfo, Primitive, Vertex}; + +/// A callback function that can be used to compose an [`epaint::PaintCallback`] for custom WGPU /// rendering. /// /// The callback is composed of two functions: `prepare` and `paint`: @@ -154,11 +153,11 @@ pub struct Renderer { /// Map of egui texture IDs to textures and their associated bindgroups (texture view + /// sampler). The texture may be None if the TextureId is just a handle to a user-provided /// sampler. - textures: HashMap, wgpu::BindGroup)>, + textures: HashMap, wgpu::BindGroup)>, next_user_texture_id: u64, - samplers: HashMap, + samplers: HashMap, - /// Storage for use by [`egui::PaintCallback`]'s that need to store resources such as render + /// Storage for use by [`epaint::PaintCallback`]'s that need to store resources such as render /// pipelines that must have the lifetime of the renderpass. pub paint_callback_resources: TypeMap, } @@ -346,7 +345,7 @@ impl Renderer { pub fn render<'rp>( &'rp self, render_pass: &mut wgpu::RenderPass<'rp>, - paint_jobs: &[egui::epaint::ClippedPrimitive], + paint_jobs: &[epaint::ClippedPrimitive], screen_descriptor: &ScreenDescriptor, ) { crate::profile_function!(); @@ -361,7 +360,7 @@ impl Renderer { let mut index_buffer_slices = self.index_buffer.slices.iter(); let mut vertex_buffer_slices = self.vertex_buffer.slices.iter(); - for egui::ClippedPrimitive { + for epaint::ClippedPrimitive { clip_rect, primitive, } in paint_jobs @@ -475,8 +474,8 @@ impl Renderer { &mut self, device: &wgpu::Device, queue: &wgpu::Queue, - id: egui::TextureId, - image_delta: &egui::epaint::ImageDelta, + id: epaint::TextureId, + image_delta: &epaint::ImageDelta, ) { crate::profile_function!(); @@ -490,7 +489,7 @@ impl Renderer { }; let data_color32 = match &image_delta.image { - egui::ImageData::Color(image) => { + epaint::ImageData::Color(image) => { assert_eq!( width as usize * height as usize, image.pixels.len(), @@ -498,7 +497,7 @@ impl Renderer { ); Cow::Borrowed(&image.pixels) } - egui::ImageData::Font(image) => { + epaint::ImageData::Font(image) => { assert_eq!( width as usize * height as usize, image.pixels.len(), @@ -555,6 +554,7 @@ impl Renderer { dimension: wgpu::TextureDimension::D2, format: wgpu::TextureFormat::Rgba8UnormSrgb, // Minspec for wgpu WebGL emulation is WebGL2, so this should always be supported. usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[wgpu::TextureFormat::Rgba8UnormSrgb], }); let sampler = self .samplers @@ -582,7 +582,7 @@ impl Renderer { }; } - pub fn free_texture(&mut self, id: &egui::TextureId) { + pub fn free_texture(&mut self, id: &epaint::TextureId) { self.textures.remove(id); } @@ -590,15 +590,15 @@ impl Renderer { /// /// This could be used by custom paint hooks to render images that have been added through with /// [`egui_extras::RetainedImage`](https://docs.rs/egui_extras/latest/egui_extras/image/struct.RetainedImage.html) - /// or [`egui::Context::load_texture`]. + /// or [`epaint::Context::load_texture`](https://docs.rs/egui/latest/egui/struct.Context.html#method.load_texture). pub fn texture( &self, - id: &egui::TextureId, + id: &epaint::TextureId, ) -> Option<&(Option, wgpu::BindGroup)> { self.textures.get(id) } - /// Registers a `wgpu::Texture` with a `egui::TextureId`. + /// Registers a `wgpu::Texture` with a `epaint::TextureId`. /// /// This enables the application to reference the texture inside an image ui element. /// This effectively enables off-screen rendering inside the egui UI. Texture must have @@ -609,7 +609,7 @@ impl Renderer { device: &wgpu::Device, texture: &wgpu::TextureView, texture_filter: wgpu::FilterMode, - ) -> egui::TextureId { + ) -> epaint::TextureId { self.register_native_texture_with_sampler_options( device, texture, @@ -622,7 +622,7 @@ impl Renderer { ) } - /// Registers a `wgpu::Texture` with an existing `egui::TextureId`. + /// Registers a `wgpu::Texture` with an existing `epaint::TextureId`. /// /// This enables applications to reuse `TextureId`s. pub fn update_egui_texture_from_wgpu_texture( @@ -630,7 +630,7 @@ impl Renderer { device: &wgpu::Device, texture: &wgpu::TextureView, texture_filter: wgpu::FilterMode, - id: egui::TextureId, + id: epaint::TextureId, ) { self.update_egui_texture_from_wgpu_texture_with_sampler_options( device, @@ -645,7 +645,7 @@ impl Renderer { ); } - /// Registers a `wgpu::Texture` with a `egui::TextureId` while also accepting custom + /// Registers a `wgpu::Texture` with a `epaint::TextureId` while also accepting custom /// `wgpu::SamplerDescriptor` options. /// /// This allows applications to specify individual minification/magnification filters as well as @@ -660,7 +660,7 @@ impl Renderer { device: &wgpu::Device, texture: &wgpu::TextureView, sampler_descriptor: wgpu::SamplerDescriptor<'_>, - ) -> egui::TextureId { + ) -> epaint::TextureId { crate::profile_function!(); let sampler = device.create_sampler(&wgpu::SamplerDescriptor { @@ -683,14 +683,14 @@ impl Renderer { ], }); - let id = egui::TextureId::User(self.next_user_texture_id); + let id = epaint::TextureId::User(self.next_user_texture_id); self.textures.insert(id, (None, bind_group)); self.next_user_texture_id += 1; id } - /// Registers a `wgpu::Texture` with an existing `egui::TextureId` while also accepting custom + /// Registers a `wgpu::Texture` with an existing `epaint::TextureId` while also accepting custom /// `wgpu::SamplerDescriptor` options. /// /// This allows applications to reuse `TextureId`s created with custom sampler options. @@ -700,7 +700,7 @@ impl Renderer { device: &wgpu::Device, texture: &wgpu::TextureView, sampler_descriptor: wgpu::SamplerDescriptor<'_>, - id: egui::TextureId, + id: epaint::TextureId, ) { crate::profile_function!(); @@ -741,7 +741,7 @@ impl Renderer { device: &wgpu::Device, queue: &wgpu::Queue, encoder: &mut wgpu::CommandEncoder, - paint_jobs: &[egui::epaint::ClippedPrimitive], + paint_jobs: &[epaint::ClippedPrimitive], screen_descriptor: &ScreenDescriptor, ) -> Vec { crate::profile_function!(); @@ -801,7 +801,7 @@ impl Renderer { let mut user_cmd_bufs = Vec::new(); // collect user command buffers crate::profile_scope!("primitives"); - for egui::ClippedPrimitive { primitive, .. } in paint_jobs.iter() { + for epaint::ClippedPrimitive { primitive, .. } in paint_jobs.iter() { match primitive { Primitive::Mesh(mesh) => { { @@ -844,14 +844,17 @@ impl Renderer { } } -fn create_sampler(options: egui::TextureOptions, device: &wgpu::Device) -> wgpu::Sampler { +fn create_sampler( + options: epaint::textures::TextureOptions, + device: &wgpu::Device, +) -> wgpu::Sampler { let mag_filter = match options.magnification { - egui::TextureFilter::Nearest => wgpu::FilterMode::Nearest, - egui::TextureFilter::Linear => wgpu::FilterMode::Linear, + epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest, + epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear, }; let min_filter = match options.minification { - egui::TextureFilter::Nearest => wgpu::FilterMode::Nearest, - egui::TextureFilter::Linear => wgpu::FilterMode::Linear, + epaint::textures::TextureFilter::Nearest => wgpu::FilterMode::Nearest, + epaint::textures::TextureFilter::Linear => wgpu::FilterMode::Linear, }; device.create_sampler(&wgpu::SamplerDescriptor { label: Some(&format!( @@ -893,7 +896,7 @@ struct ScissorRect { } impl ScissorRect { - fn new(clip_rect: &egui::Rect, pixels_per_point: f32, target_size: [u32; 2]) -> Self { + fn new(clip_rect: &epaint::Rect, pixels_per_point: f32, target_size: [u32; 2]) -> Self { // Transform clip rect to physical pixels: let clip_min_x = pixels_per_point * clip_rect.min.x; let clip_min_y = pixels_per_point * clip_rect.min.y; diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index 8eb788dc..d10f1194 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -1,9 +1,10 @@ use std::sync::Arc; -use egui::mutex::RwLock; use tracing::error; use wgpu::{Adapter, Instance, Surface}; +use epaint::mutex::RwLock; + use crate::{renderer, RenderState, Renderer, SurfaceErrorAction, WgpuConfiguration}; struct SurfaceState { @@ -41,12 +42,15 @@ impl Painter { /// a [`winit::window::Window`] with a valid `.raw_window_handle()` /// associated. pub fn new(configuration: WgpuConfiguration, msaa_samples: u32, depth_bits: u8) -> Self { - let instance = wgpu::Instance::new(configuration.backends); + let instance = wgpu::Instance::new(wgpu::InstanceDescriptor { + backends: configuration.backends, + dx12_shader_compiler: Default::default(), // + }); Self { configuration, msaa_samples, - depth_format: (depth_bits > 0).then(|| wgpu::TextureFormat::Depth32Float), + depth_format: (depth_bits > 0).then_some(wgpu::TextureFormat::Depth32Float), depth_texture_view: None, instance, @@ -60,26 +64,27 @@ impl Painter { /// /// Will return [`None`] if the render state has not been initialized yet. pub fn render_state(&self) -> Option { - self.render_state.as_ref().cloned() + self.render_state.clone() } async fn init_render_state( &self, adapter: &Adapter, target_format: wgpu::TextureFormat, - ) -> RenderState { - let (device, queue) = - pollster::block_on(adapter.request_device(&self.configuration.device_descriptor, None)) - .unwrap(); - - let renderer = Renderer::new(&device, target_format, self.depth_format, self.msaa_samples); - - RenderState { - device: Arc::new(device), - queue: Arc::new(queue), - target_format, - renderer: Arc::new(RwLock::new(renderer)), - } + ) -> Result { + adapter + .request_device(&self.configuration.device_descriptor, None) + .await + .map(|(device, queue)| { + let renderer = + Renderer::new(&device, target_format, self.depth_format, self.msaa_samples); + RenderState { + device: Arc::new(device), + queue: Arc::new(queue), + target_format, + renderer: Arc::new(RwLock::new(renderer)), + } + }) } // We want to defer the initialization of our render state until we have a surface @@ -87,25 +92,33 @@ impl Painter { // // After we've initialized our render state once though we expect all future surfaces // will have the same format and so this render state will remain valid. - fn ensure_render_state_for_surface(&mut self, surface: &Surface) { - self.adapter.get_or_insert_with(|| { - pollster::block_on(self.instance.request_adapter(&wgpu::RequestAdapterOptions { - power_preference: self.configuration.power_preference, - compatible_surface: Some(surface), - force_fallback_adapter: false, - })) - .unwrap() - }); - - if self.render_state.is_none() { - let adapter = self.adapter.as_ref().unwrap(); - - let swapchain_format = - crate::preferred_framebuffer_format(&surface.get_supported_formats(adapter)); - - let rs = pollster::block_on(self.init_render_state(adapter, swapchain_format)); - self.render_state = Some(rs); + async fn ensure_render_state_for_surface( + &mut self, + surface: &Surface, + ) -> Result<(), wgpu::RequestDeviceError> { + if self.adapter.is_none() { + self.adapter = self + .instance + .request_adapter(&wgpu::RequestAdapterOptions { + power_preference: self.configuration.power_preference, + compatible_surface: Some(surface), + force_fallback_adapter: false, + }) + .await; } + if self.render_state.is_none() { + match &self.adapter { + Some(adapter) => { + let swapchain_format = crate::preferred_framebuffer_format( + &surface.get_capabilities(adapter).formats, + ); + let rs = self.init_render_state(adapter, swapchain_format).await?; + self.render_state = Some(rs); + } + None => return Err(wgpu::RequestDeviceError {}), + } + } + Ok(()) } fn configure_surface(&mut self, width_in_pixels: u32, height_in_pixels: u32) { @@ -124,6 +137,7 @@ impl Painter { height: height_in_pixels, present_mode: self.configuration.present_mode, alpha_mode: wgpu::CompositeAlphaMode::Auto, + view_formats: vec![format], }; let surface_state = self @@ -161,12 +175,18 @@ impl Painter { /// The raw Window handle associated with the given `window` must be a valid object to create a /// surface upon and must remain valid for the lifetime of the created surface. (The surface may /// be cleared by passing `None`). - pub unsafe fn set_window(&mut self, window: Option<&winit::window::Window>) { + /// + /// # Errors + /// If the provided wgpu configuration does not match an available device. + pub async unsafe fn set_window( + &mut self, + window: Option<&winit::window::Window>, + ) -> Result<(), crate::WgpuError> { match window { Some(window) => { - let surface = self.instance.create_surface(&window); + let surface = self.instance.create_surface(&window)?; - self.ensure_render_state_for_surface(&surface); + self.ensure_render_state_for_surface(&surface).await?; let size = window.inner_size(); let width = size.width; @@ -176,12 +196,13 @@ impl Painter { width, height, }); - self.configure_surface(width, height); + self.resize_and_generate_depth_texture_view(width, height); } None => { self.surface_state = None; } } + Ok(()) } /// Returns the maximum texture dimension supported if known @@ -195,28 +216,37 @@ impl Painter { .map(|rs| rs.device.limits().max_texture_dimension_2d as usize) } + fn resize_and_generate_depth_texture_view( + &mut self, + width_in_pixels: u32, + height_in_pixels: u32, + ) { + self.configure_surface(width_in_pixels, height_in_pixels); + let device = &self.render_state.as_ref().unwrap().device; + self.depth_texture_view = self.depth_format.map(|depth_format| { + device + .create_texture(&wgpu::TextureDescriptor { + label: Some("egui_depth_texture"), + size: wgpu::Extent3d { + width: width_in_pixels, + height: height_in_pixels, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: depth_format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT + | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[depth_format], + }) + .create_view(&wgpu::TextureViewDescriptor::default()) + }); + } + pub fn on_window_resized(&mut self, width_in_pixels: u32, height_in_pixels: u32) { if self.surface_state.is_some() { - self.configure_surface(width_in_pixels, height_in_pixels); - let device = &self.render_state.as_ref().unwrap().device; - self.depth_texture_view = self.depth_format.map(|depth_format| { - device - .create_texture(&wgpu::TextureDescriptor { - label: Some("egui_depth_texture"), - size: wgpu::Extent3d { - width: width_in_pixels, - height: height_in_pixels, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: depth_format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT - | wgpu::TextureUsages::TEXTURE_BINDING, - }) - .create_view(&wgpu::TextureViewDescriptor::default()) - }); + self.resize_and_generate_depth_texture_view(width_in_pixels, height_in_pixels); } else { error!("Ignoring window resize notification with no surface created via Painter::set_window()"); } @@ -225,9 +255,9 @@ impl Painter { pub fn paint_and_update_textures( &mut self, pixels_per_point: f32, - clear_color: egui::Rgba, - clipped_primitives: &[egui::ClippedPrimitive], - textures_delta: &egui::TexturesDelta, + clear_color: [f32; 4], + clipped_primitives: &[epaint::ClippedPrimitive], + textures_delta: &epaint::textures::TexturesDelta, ) { crate::profile_function!(); @@ -305,10 +335,10 @@ impl Painter { resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear(wgpu::Color { - r: clear_color.r() as f64, - g: clear_color.g() as f64, - b: clear_color.b() as f64, - a: clear_color.a() as f64, + r: clear_color[0] as f64, + g: clear_color[1] as f64, + b: clear_color[2] as f64, + a: clear_color[3] as f64, }), store: true, }, diff --git a/crates/egui-winit/CHANGELOG.md b/crates/egui-winit/CHANGELOG.md index 317786e3..91b24cf1 100644 --- a/crates/egui-winit/CHANGELOG.md +++ b/crates/egui-winit/CHANGELOG.md @@ -3,8 +3,19 @@ All notable changes to the `egui-winit` integration will be noted in this file. ## Unreleased -* The default features of the `winit` crate are not enabled if the default features of `egui-winit` are disabled too ([#1971](https://github.com/emilk/egui/pull/1971)) -* Added new feature `wayland` which enables Wayland support ([#1971](https://github.com/emilk/egui/pull/1971)) +* Update to `winit` 0.28, adding support for mac trackpad zoom ([#2654](https://github.com/emilk/egui/pull/2654)). +* Remove the `screen_reader` feature. Use the `accesskit` feature flag instead ([#2669](https://github.com/emilk/egui/pull/2669)). + + +## 0.20.1 - 2022-12-11 +* Fix docs.rs build ([#2420](https://github.com/emilk/egui/pull/2420)). + + +## 0.20.0 - 2022-12-08 +* The default features of the `winit` crate are not enabled if the default features of `egui-winit` are disabled too ([#1971](https://github.com/emilk/egui/pull/1971)). +* Added new feature `wayland` which enables Wayland support ([#1971](https://github.com/emilk/egui/pull/1971)). +* Don't repaint when just moving window ([#1980](https://github.com/emilk/egui/pull/1980)). +* Added optional integration with [AccessKit](https://accesskit.dev/) for implementing platform accessibility APIs ([#2294](https://github.com/emilk/egui/pull/2294)). ## 0.19.0 - 2022-08-20 * MSRV (Minimum Supported Rust Version) is now `1.61.0` ([#1846](https://github.com/emilk/egui/pull/1846)). diff --git a/crates/egui-winit/Cargo.toml b/crates/egui-winit/Cargo.toml index 70cb6f52..576f3daf 100644 --- a/crates/egui-winit/Cargo.toml +++ b/crates/egui-winit/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "egui-winit" -version = "0.19.0" +version = "0.20.1" authors = ["Emil Ernerfeldt "] description = "Bindings for using egui with winit" edition = "2021" -rust-version = "1.62" +rust-version = "1.65" homepage = "https://github.com/emilk/egui/tree/master/crates/egui-winit" license = "MIT OR Apache-2.0" readme = "README.md" @@ -20,6 +20,9 @@ all-features = true [features] default = ["clipboard", "links", "wayland", "winit/default"] +## Enable platform accessibility API implementations through [AccessKit](https://accesskit.dev/). +accesskit = ["accesskit_winit", "egui/accesskit"] + ## [`bytemuck`](https://docs.rs/bytemuck) enables you to cast [`egui::epaint::Vertex`], [`egui::Vec2`] etc to `&[u8]`. bytemuck = ["egui/bytemuck"] @@ -33,9 +36,6 @@ links = ["webbrowser"] ## Enable profiling with the [`puffin`](https://docs.rs/puffin) crate. puffin = ["dep:puffin"] -## Experimental support for a screen reader. -screen_reader = ["tts"] - ## Allow serialization of [`WindowSettings`] using [`serde`](https://docs.rs/serde). serde = ["egui/serde", "dep:serde"] @@ -43,30 +43,34 @@ serde = ["egui/serde", "dep:serde"] wayland = ["winit/wayland"] [dependencies] -egui = { version = "0.19.0", path = "../egui", default-features = false, features = [ +egui = { version = "0.20.0", path = "../egui", default-features = false, features = [ "tracing", ] } instant = { version = "0.1", features = [ "wasm-bindgen", ] } # We use instant so we can (maybe) compile for web tracing = { version = "0.1", default-features = false, features = ["std"] } -winit = { version = "0.27.2", default-features = false } +winit = { version = "0.28", default-features = false } #! ### Optional dependencies +# feature accesskit +accesskit_winit = { version = "0.9.0", optional = true } + ## Enable this when generating docs. document-features = { version = "0.2", optional = true } puffin = { version = "0.14", optional = true } serde = { version = "1.0", optional = true, features = ["derive"] } -# feature screen_reader -tts = { version = "0.24", optional = true } - -webbrowser = { version = "0.8", optional = true } +webbrowser = { version = "0.8.3", optional = true } [target.'cfg(any(target_os="linux", target_os="dragonfly", target_os="freebsd", target_os="netbsd", target_os="openbsd"))'.dependencies] smithay-clipboard = { version = "0.6.3", optional = true } [target.'cfg(not(target_os = "android"))'.dependencies] arboard = { version = "3.2", optional = true, default-features = false } + +[target.'cfg(target_os = "android")'.dependencies] +# TODO(emilk): this is probably not the right place for specifying native-activity, but we need to do it somewhere for the CI +android-activity = { version = "0.4", features = ["native-activity"] } diff --git a/crates/egui-winit/src/clipboard.rs b/crates/egui-winit/src/clipboard.rs index 576a78e5..b2bd6303 100644 --- a/crates/egui-winit/src/clipboard.rs +++ b/crates/egui-winit/src/clipboard.rs @@ -30,6 +30,7 @@ impl Clipboard { Self { #[cfg(all(feature = "arboard", not(target_os = "android")))] arboard: init_arboard(), + #[cfg(all( any( target_os = "linux", @@ -41,6 +42,7 @@ impl Clipboard { feature = "smithay-clipboard" ))] smithay: init_smithay_clipboard(wayland_display), + clipboard: Default::default(), } } @@ -60,7 +62,7 @@ impl Clipboard { return match clipboard.load() { Ok(text) => Some(text), Err(err) => { - tracing::error!("Paste error: {}", err); + tracing::error!("smithay paste error: {err}"); None } }; @@ -71,7 +73,7 @@ impl Clipboard { return match clipboard.get_text() { Ok(text) => Some(text), Err(err) => { - tracing::error!("Paste error: {}", err); + tracing::error!("arboard paste error: {err}"); None } }; @@ -99,7 +101,7 @@ impl Clipboard { #[cfg(all(feature = "arboard", not(target_os = "android")))] if let Some(clipboard) = &mut self.arboard { if let Err(err) = clipboard.set_text(text) { - tracing::error!("Copy/Cut error: {}", err); + tracing::error!("arboard copy/cut error: {err}"); } return; } @@ -110,10 +112,11 @@ impl Clipboard { #[cfg(all(feature = "arboard", not(target_os = "android")))] fn init_arboard() -> Option { + tracing::debug!("Initializing arboard clipboardโ€ฆ"); match arboard::Clipboard::new() { Ok(clipboard) => Some(clipboard), Err(err) => { - tracing::error!("Failed to initialize clipboard: {}", err); + tracing::warn!("Failed to initialize arboard clipboard: {err}"); None } } @@ -133,10 +136,11 @@ fn init_smithay_clipboard( wayland_display: Option<*mut c_void>, ) -> Option { if let Some(display) = wayland_display { + tracing::debug!("Initializing smithay clipboardโ€ฆ"); #[allow(unsafe_code)] Some(unsafe { smithay_clipboard::Clipboard::new(display) }) } else { - tracing::error!("Cannot initialize smithay clipboard without a display handle!"); + tracing::debug!("Cannot initialize smithay clipboard without a display handle"); None } } diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 6dde4124..05b0cf8a 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -11,27 +11,20 @@ use std::os::raw::c_void; +#[cfg(feature = "accesskit")] +pub use accesskit_winit; pub use egui; +#[cfg(feature = "accesskit")] +use egui::accesskit; pub use winit; pub mod clipboard; -pub mod screen_reader; mod window_settings; pub use window_settings::WindowSettings; use winit::event_loop::EventLoopWindowTarget; -#[cfg(feature = "wayland")] -#[cfg(any( - target_os = "linux", - target_os = "dragonfly", - target_os = "freebsd", - target_os = "netbsd", - target_os = "openbsd" -))] -use winit::platform::unix::EventLoopWindowTargetExtUnix; - pub fn native_pixels_per_point(window: &winit::window::Window) -> f32 { window.scale_factor() as f32 } @@ -71,7 +64,6 @@ pub struct State { current_pixels_per_point: f32, clipboard: clipboard::Clipboard, - screen_reader: screen_reader::ScreenReader, /// If `true`, mouse inputs will be treated as touches. /// Useful for debugging touch support in egui. @@ -86,6 +78,9 @@ pub struct State { /// track ime state input_method_editor_started: bool, + + #[cfg(feature = "accesskit")] + accesskit: Option, } impl State { @@ -108,15 +103,31 @@ impl State { current_pixels_per_point: 1.0, clipboard: clipboard::Clipboard::new(wayland_display), - screen_reader: screen_reader::ScreenReader::default(), simulate_touch_screen: false, pointer_touch_id: None, input_method_editor_started: false, + + #[cfg(feature = "accesskit")] + accesskit: None, } } + #[cfg(feature = "accesskit")] + pub fn init_accesskit + Send>( + &mut self, + window: &winit::window::Window, + event_loop_proxy: winit::event_loop::EventLoopProxy, + initial_tree_update_factory: impl 'static + FnOnce() -> accesskit::TreeUpdate + Send, + ) { + self.accesskit = Some(accesskit_winit::Adapter::new( + window, + initial_tree_update_factory, + event_loop_proxy, + )); + } + /// Call this once a graphics context has been created to update the maximum texture dimensions /// that egui will use. pub fn set_max_texture_side(&mut self, max_texture_side: usize) { @@ -356,8 +367,9 @@ impl State { consumed: false, } } - WindowEvent::AxisMotion { .. } - | WindowEvent::CloseRequested + + // Things that may require repaint: + WindowEvent::CloseRequested | WindowEvent::CursorEntered { .. } | WindowEvent::Destroyed | WindowEvent::Occluded(_) @@ -367,13 +379,39 @@ impl State { repaint: true, consumed: false, }, - WindowEvent::Moved(_) => EventResponse { - repaint: false, // moving a window doesn't warrant a repaint + + // Things we completely ignore: + WindowEvent::AxisMotion { .. } + | WindowEvent::Moved(_) + | WindowEvent::SmartMagnify { .. } + | WindowEvent::TouchpadRotate { .. } => EventResponse { + repaint: false, consumed: false, }, + + WindowEvent::TouchpadMagnify { delta, .. } => { + // Positive delta values indicate magnification (zooming in). + // Negative delta values indicate shrinking (zooming out). + let zoom_factor = (*delta as f32).exp(); + self.egui_input.events.push(egui::Event::Zoom(zoom_factor)); + EventResponse { + repaint: true, + consumed: egui_ctx.wants_pointer_input(), + } + } } } + /// Call this when there is a new [`accesskit::ActionRequest`]. + /// + /// The result can be found in [`Self::egui_input`] and be extracted with [`Self::take_egui_input`]. + #[cfg(feature = "accesskit")] + pub fn on_accesskit_action_request(&mut self, request: accesskit::ActionRequest) { + self.egui_input + .events + .push(egui::Event::AccessKitActionRequest(request)); + } + fn on_mouse_button_input( &mut self, state: winit::event::ElementState, @@ -560,6 +598,7 @@ impl State { self.egui_input.events.push(egui::Event::Key { key, pressed, + repeat: false, // egui will fill this in for us! modifiers: self.egui_input.modifiers, }); } @@ -580,11 +619,6 @@ impl State { egui_ctx: &egui::Context, platform_output: egui::PlatformOutput, ) { - if egui_ctx.options().screen_reader { - self.screen_reader - .speak(&platform_output.events_description()); - } - let egui::PlatformOutput { cursor_icon, open_url, @@ -592,6 +626,8 @@ impl State { events: _, // handled above mutable_text_under_cursor: _, // only used in eframe web text_cursor_pos, + #[cfg(feature = "accesskit")] + accesskit_update, } = platform_output; self.current_pixels_per_point = egui_ctx.pixels_per_point(); // someone can have changed it to scale the UI @@ -608,11 +644,18 @@ impl State { if let Some(egui::Pos2 { x, y }) = text_cursor_pos { window.set_ime_position(winit::dpi::LogicalPosition { x, y }); } + + #[cfg(feature = "accesskit")] + if let Some(accesskit) = self.accesskit.as_ref() { + if let Some(update) = accesskit_update { + accesskit.update_if_active(|| update); + } + } } fn set_cursor_icon(&mut self, window: &winit::window::Window, cursor_icon: egui::CursorIcon) { - // prevent flickering near frame boundary when Windows OS tries to control cursor icon for window resizing - #[cfg(windows)] + // Prevent flickering near frame boundary when Windows OS tries to control cursor icon for window resizing. + // On other platforms: just early-out to save CPU. if self.current_cursor_icon == cursor_icon { return; } @@ -835,6 +878,7 @@ fn wayland_display(_event_loop: &EventLoopWindowTarget) -> Option<*mut c_v target_os = "openbsd" ))] { + use winit::platform::wayland::EventLoopWindowTargetExtWayland as _; return _event_loop.wayland_display(); } diff --git a/crates/egui-winit/src/screen_reader.rs b/crates/egui-winit/src/screen_reader.rs deleted file mode 100644 index 058407b7..00000000 --- a/crates/egui-winit/src/screen_reader.rs +++ /dev/null @@ -1,49 +0,0 @@ -pub struct ScreenReader { - #[cfg(feature = "tts")] - tts: Option, -} - -#[cfg(not(feature = "tts"))] -#[allow(clippy::derivable_impls)] // False positive -impl Default for ScreenReader { - fn default() -> Self { - Self {} - } -} - -#[cfg(feature = "tts")] -impl Default for ScreenReader { - fn default() -> Self { - let tts = match tts::Tts::default() { - Ok(screen_reader) => { - tracing::debug!("Initialized screen reader."); - Some(screen_reader) - } - Err(err) => { - tracing::warn!("Failed to load screen reader: {}", err); - None - } - }; - Self { tts } - } -} - -impl ScreenReader { - #[cfg(not(feature = "tts"))] - #[allow(clippy::unused_self)] - pub fn speak(&mut self, _text: &str) {} - - #[cfg(feature = "tts")] - pub fn speak(&mut self, text: &str) { - if text.is_empty() { - return; - } - if let Some(tts) = &mut self.tts { - tracing::debug!("Speaking: {:?}", text); - let interrupt = true; - if let Err(err) = tts.speak(text, interrupt) { - tracing::warn!("Failed to read: {}", err); - } - } - } -} diff --git a/crates/egui-winit/src/window_settings.rs b/crates/egui-winit/src/window_settings.rs index 78aeace0..d7d099c8 100644 --- a/crates/egui-winit/src/window_settings.rs +++ b/crates/egui-winit/src/window_settings.rs @@ -42,16 +42,21 @@ impl WindowSettings { } } + pub fn inner_size_points(&self) -> Option { + self.inner_size_points + } + pub fn initialize_window( &self, mut window: winit::window::WindowBuilder, ) -> winit::window::WindowBuilder { - if !cfg!(target_os = "windows") { - // If the app last ran on two monitors and only one is now connected, then - // the given position is invalid. - // If this happens on Mac, the window is clamped into valid area. - // If this happens on Windows, the window is hidden and very difficult to find. - // So we don't restore window positions on Windows. + // If the app last ran on two monitors and only one is now connected, then + // the given position is invalid. + // If this happens on Mac, the window is clamped into valid area. + // If this happens on Windows, the window is hidden and very difficult to find. + // So we don't restore window positions on Windows. + let try_restore_position = !cfg!(target_os = "windows"); + if try_restore_position { if let Some(pos) = self.position { window = window.with_position(winit::dpi::PhysicalPosition { x: pos.x as f64, @@ -68,10 +73,21 @@ impl WindowSettings { }) .with_fullscreen( self.fullscreen - .then(|| winit::window::Fullscreen::Borderless(None)), + .then_some(winit::window::Fullscreen::Borderless(None)), ) } else { window } } + + pub fn clamp_to_sane_values(&mut self, max_size: egui::Vec2) { + use egui::NumExt as _; + + if let Some(size) = &mut self.inner_size_points { + // Prevent ridiculously small windows + let min_size = egui::Vec2::splat(64.0); + *size = size.at_least(min_size); + *size = size.at_most(max_size); + } + } } diff --git a/crates/egui/Cargo.toml b/crates/egui/Cargo.toml index cf13c969..1920a855 100644 --- a/crates/egui/Cargo.toml +++ b/crates/egui/Cargo.toml @@ -1,10 +1,10 @@ [package] name = "egui" -version = "0.19.0" +version = "0.20.1" authors = ["Emil Ernerfeldt "] description = "An easy-to-use immediate mode GUI that runs on both web and native" edition = "2021" -rust-version = "1.62" +rust-version = "1.65" homepage = "https://github.com/emilk/egui" license = "MIT OR Apache-2.0" readme = "../../README.md" @@ -52,10 +52,10 @@ mint = ["epaint/mint"] persistence = ["serde", "epaint/serde", "ron"] ## Allow serialization using [`serde`](https://docs.rs/serde). -serde = ["dep:serde", "epaint/serde"] +serde = ["dep:serde", "epaint/serde", "accesskit?/serde"] [dependencies] -epaint = { version = "0.19.0", path = "../epaint", default-features = false } +epaint = { version = "0.20.0", path = "../epaint", default-features = false } ahash = { version = "0.8.1", default-features = false, features = [ "no-rng", # we don't need DOS-protection, so we let users opt-in to it instead @@ -64,6 +64,10 @@ ahash = { version = "0.8.1", default-features = false, features = [ nohash-hasher = "0.2" #! ### Optional dependencies +## Exposes detailed accessibility implementation required by platform +## accessibility APIs. Also requires support in the egui integration. +accesskit = { version = "0.8.1", optional = true } + ## Enable this when generating docs. document-features = { version = "0.2", optional = true } diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index 072af44e..84f0ea44 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -5,7 +5,7 @@ use crate::*; /// State that is persisted between frames. -// TODO(emilk): this is not currently stored in `memory().data`, but maybe it should be? +// TODO(emilk): this is not currently stored in `Memory::data`, but maybe it should be? #[derive(Clone, Copy, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub(crate) struct State { @@ -192,11 +192,12 @@ pub(crate) struct Prepared { move_response: Response, enabled: bool, drag_bounds: Option, - /// Set the first frame of new windows with anchors. + + /// We always make windows invisible the first frame to hide "first-frame-jitters". /// /// This is so that we use the first frame to calculate the window size, - /// and then can correctly position the window the next frame, - /// without having one frame where the window is positioned in the wrong place. + /// and then can correctly position the window and its contents the next frame, + /// without having one frame where the window is wrongly positioned or sized. temporarily_invisible: bool, } @@ -230,7 +231,7 @@ impl Area { let layer_id = LayerId::new(order, id); - let state = ctx.memory().areas.get(id).cloned(); + let state = ctx.memory(|mem| mem.areas.get(id).copied()); let is_new = state.is_none(); if is_new { ctx.request_repaint(); // if we don't know the previous size we are likely drawing the area in the wrong place @@ -242,24 +243,15 @@ impl Area { }); state.pos = new_pos.unwrap_or(state.pos); state.interactable = interactable; - let mut temporarily_invisible = false; if pivot != Align2::LEFT_TOP { - if is_new { - temporarily_invisible = true; // figure out the size first - } else { - state.pos.x -= pivot.x().to_factor() * state.size.x; - state.pos.y -= pivot.y().to_factor() * state.size.y; - } + state.pos.x -= pivot.x().to_factor() * state.size.x; + state.pos.y -= pivot.y().to_factor() * state.size.y; } if let Some((anchor, offset)) = anchor { - if is_new { - temporarily_invisible = true; // figure out the size first - } else { - let screen = ctx.available_rect(); - state.pos = anchor.align_size_within_rect(state.size, screen).min + offset; - } + let screen = ctx.available_rect(); + state.pos = anchor.align_size_within_rect(state.size, screen).min + offset; } // interact right away to prevent frame-delay @@ -286,7 +278,7 @@ impl Area { // Important check - don't try to move e.g. a combobox popup! if movable { if move_response.dragged() { - state.pos += ctx.input().pointer.delta(); + state.pos += ctx.input(|i| i.pointer.delta()); } state.pos = ctx @@ -296,9 +288,9 @@ impl Area { if (move_response.dragged() || move_response.clicked()) || pointer_pressed_on_area(ctx, layer_id) - || !ctx.memory().areas.visible_last_frame(&layer_id) + || !ctx.memory(|m| m.areas.visible_last_frame(&layer_id)) { - ctx.memory().areas.move_to_top(layer_id); + ctx.memory_mut(|m| m.areas.move_to_top(layer_id)); ctx.request_repaint(); } @@ -319,7 +311,7 @@ impl Area { move_response, enabled, drag_bounds, - temporarily_invisible, + temporarily_invisible: is_new, } } @@ -337,7 +329,7 @@ impl Area { } let layer_id = LayerId::new(self.order, self.id); - let area_rect = ctx.memory().areas.get(self.id).map(|area| area.rect()); + let area_rect = ctx.memory(|mem| mem.areas.get(self.id).map(|area| area.rect())); if let Some(area_rect) = area_rect { let clip_rect = ctx.available_rect(); let painter = Painter::new(ctx.clone(), layer_id, clip_rect); @@ -366,7 +358,7 @@ impl Prepared { } pub(crate) fn content_ui(&self, ctx: &Context) -> Ui { - let screen_rect = ctx.input().screen_rect(); + let screen_rect = ctx.screen_rect(); let bounds = if let Some(bounds) = self.drag_bounds { bounds.intersect(screen_rect) // protect against infinite bounds @@ -418,7 +410,7 @@ impl Prepared { state.size = content_ui.min_rect().size(); - ctx.memory().areas.set_state(layer_id, state); + ctx.memory_mut(|m| m.areas.set_state(layer_id, state)); move_response } @@ -426,7 +418,7 @@ impl Prepared { fn pointer_pressed_on_area(ctx: &Context, layer_id: LayerId) -> bool { if let Some(pointer_pos) = ctx.pointer_interact_pos() { - let any_pressed = ctx.input().pointer.any_pressed(); + let any_pressed = ctx.input(|i| i.pointer.any_pressed()); any_pressed && ctx.layer_id_at(pointer_pos) == Some(layer_id) } else { false @@ -434,13 +426,13 @@ fn pointer_pressed_on_area(ctx: &Context, layer_id: LayerId) -> bool { } fn automatic_area_position(ctx: &Context) -> Pos2 { - let mut existing: Vec = ctx - .memory() - .areas - .visible_windows() - .into_iter() - .map(State::rect) - .collect(); + let mut existing: Vec = ctx.memory(|mem| { + mem.areas + .visible_windows() + .into_iter() + .map(State::rect) + .collect() + }); existing.sort_by_key(|r| r.left().round() as i32); let available_rect = ctx.available_rect(); diff --git a/crates/egui/src/containers/collapsing_header.rs b/crates/egui/src/containers/collapsing_header.rs index febf0d1b..7980f4be 100644 --- a/crates/egui/src/containers/collapsing_header.rs +++ b/crates/egui/src/containers/collapsing_header.rs @@ -26,13 +26,14 @@ pub struct CollapsingState { impl CollapsingState { pub fn load(ctx: &Context, id: Id) -> Option { - ctx.data() - .get_persisted::(id) - .map(|state| Self { id, state }) + ctx.data_mut(|d| { + d.get_persisted::(id) + .map(|state| Self { id, state }) + }) } pub fn store(&self, ctx: &Context) { - ctx.data().insert_persisted(self.id, self.state); + ctx.data_mut(|d| d.insert_persisted(self.id, self.state)); } pub fn id(&self) -> Id { @@ -64,7 +65,7 @@ impl CollapsingState { /// 0 for closed, 1 for open, with tweening pub fn openness(&self, ctx: &Context) -> f32 { - if ctx.memory().everything_is_visible() { + if ctx.memory(|mem| mem.everything_is_visible()) { 1.0 } else { ctx.animate_bool(self.id, self.state.open) @@ -111,10 +112,7 @@ impl CollapsingState { response.rect.center().y, )); let openness = self.openness(ui.ctx()); - let small_icon_response = Response { - rect: icon_rect, - ..response.clone() - }; + let small_icon_response = response.clone().with_new_rect(icon_rect); icon_fn(ui, openness, &small_icon_response); response } @@ -311,7 +309,6 @@ impl<'ui, HeaderRet> HeaderResponse<'ui, HeaderRet> { /// Paint the arrow icon that indicated if the region is open or not pub fn paint_default_icon(ui: &mut Ui, openness: f32, response: &Response) { let visuals = ui.style().interact(response); - let stroke = visuals.fg_stroke; let rect = response.rect; @@ -325,7 +322,11 @@ pub fn paint_default_icon(ui: &mut Ui, openness: f32, response: &Response) { *p = rect.center() + rotation * (*p - rect.center()); } - ui.painter().add(Shape::closed_line(points, stroke)); + ui.painter().add(Shape::convex_polygon( + points, + visuals.fg_stroke.color, + Stroke::NONE, + )); } /// A function that paints an icon indicating if the region is open or not @@ -552,7 +553,7 @@ impl CollapsingHeader { ui.painter().add(epaint::RectShape { rect: header_response.rect.expand(visuals.expansion), rounding: visuals.rounding, - fill: visuals.bg_fill, + fill: visuals.weak_bg_fill, stroke: visuals.bg_stroke, // stroke: Default::default(), }); @@ -572,10 +573,7 @@ impl CollapsingHeader { header_response.rect.left() + ui.spacing().indent / 2.0, header_response.rect.center().y, )); - let icon_response = Response { - rect: icon_rect, - ..header_response.clone() - }; + let icon_response = header_response.clone().with_new_rect(icon_rect); if let Some(icon) = icon { icon(ui, openness, &icon_response); } else { diff --git a/crates/egui/src/containers/combo_box.rs b/crates/egui/src/containers/combo_box.rs index 9c33002f..8ede4d67 100644 --- a/crates/egui/src/containers/combo_box.rs +++ b/crates/egui/src/containers/combo_box.rs @@ -36,6 +36,7 @@ pub struct ComboBox { selected_text: WidgetText, width: Option, icon: Option, + wrap_enabled: bool, } impl ComboBox { @@ -47,6 +48,7 @@ impl ComboBox { selected_text: Default::default(), width: None, icon: None, + wrap_enabled: false, } } @@ -59,6 +61,7 @@ impl ComboBox { selected_text: Default::default(), width: None, icon: None, + wrap_enabled: false, } } @@ -70,10 +73,11 @@ impl ComboBox { selected_text: Default::default(), width: None, icon: None, + wrap_enabled: false, } } - /// Set the width of the button and menu + /// Set the outer width of the button and menu. pub fn width(mut self, width: f32) -> Self { self.width = Some(width); self @@ -124,6 +128,12 @@ impl ComboBox { self } + /// Controls whether text wrap is used for the selected text + pub fn wrap(mut self, wrap: bool) -> Self { + self.wrap_enabled = wrap; + self + } + /// Show the combo box, with the given ui code for the menu contents. /// /// Returns `InnerResponse { inner: None }` if the combo box is closed. @@ -146,15 +156,21 @@ impl ComboBox { selected_text, width, icon, + wrap_enabled, } = self; let button_id = ui.make_persistent_id(id_source); ui.horizontal(|ui| { - if let Some(width) = width { - ui.spacing_mut().slider_width = width; // yes, this is ugly. Will remove later. - } - let mut ir = combo_box_dyn(ui, button_id, selected_text, menu_contents, icon); + let mut ir = combo_box_dyn( + ui, + button_id, + selected_text, + menu_contents, + icon, + wrap_enabled, + width, + ); if let Some(label) = label { ir.response .widget_info(|| WidgetInfo::labeled(WidgetType::ComboBox, label.text())); @@ -221,35 +237,59 @@ fn combo_box_dyn<'c, R>( selected_text: WidgetText, menu_contents: Box R + 'c>, icon: Option, + wrap_enabled: bool, + width: Option, ) -> InnerResponse> { let popup_id = button_id.with("popup"); - let is_popup_open = ui.memory().is_popup_open(popup_id); + let is_popup_open = ui.memory(|m| m.is_popup_open(popup_id)); - let popup_height = ui - .ctx() - .memory() - .areas - .get(popup_id) - .map_or(100.0, |state| state.size.y); + let popup_height = ui.memory(|m| m.areas.get(popup_id).map_or(100.0, |state| state.size.y)); let above_or_below = if ui.next_widget_position().y + ui.spacing().interact_size.y + popup_height - < ui.ctx().input().screen_rect().bottom() + < ui.ctx().screen_rect().bottom() { AboveOrBelow::Below } else { AboveOrBelow::Above }; + let margin = ui.spacing().button_padding; let button_response = button_frame(ui, button_id, is_popup_open, Sense::click(), |ui| { + let icon_spacing = ui.spacing().icon_spacing; // We don't want to change width when user selects something new - let full_minimum_width = ui.spacing().slider_width; + let full_minimum_width = if wrap_enabled { + // Currently selected value's text will be wrapped if needed, so occupy the available width. + ui.available_width() + } else { + // Occupy at least the minimum width assigned to ComboBox. + let width = width.unwrap_or_else(|| ui.spacing().combo_width); + width - 2.0 * margin.x + }; let icon_size = Vec2::splat(ui.spacing().icon_width); + let wrap_width = if wrap_enabled { + // Use the available width, currently selected value's text will be wrapped if exceeds this value. + ui.available_width() - icon_spacing - icon_size.x + } else { + // Use all the width necessary to display the currently selected value's text. + f32::INFINITY + }; - let galley = selected_text.into_galley(ui, Some(false), f32::INFINITY, TextStyle::Button); + let galley = + selected_text.into_galley(ui, Some(wrap_enabled), wrap_width, TextStyle::Button); - let width = galley.size().x + ui.spacing().item_spacing.x + icon_size.x; + // The width necessary to contain the whole widget with the currently selected value's text. + let width = if wrap_enabled { + full_minimum_width + } else { + // Occupy at least the minimum width needed to contain the widget with the currently selected value's text. + galley.size().x + icon_spacing + icon_size.x + }; + + // Case : wrap_enabled : occupy all the available width. + // Case : !wrap_enabled : occupy at least the minimum width assigned to Slider and ComboBox, + // increase if the currently selected value needs additional horizontal space to fully display its text (up to wrap_width (f32::INFINITY)). let width = width.at_least(full_minimum_width); let height = galley.size().y.max(icon_size.y); @@ -289,7 +329,7 @@ fn combo_box_dyn<'c, R>( }); if button_response.clicked() { - ui.memory().toggle_popup(popup_id); + ui.memory_mut(|mem| mem.toggle_popup(popup_id)); } let inner = crate::popup::popup_above_or_below_widget( ui, @@ -346,7 +386,7 @@ fn button_frame( epaint::RectShape { rect: outer_rect.expand(visuals.expansion), rounding: visuals.rounding, - fill: visuals.bg_fill, + fill: visuals.weak_bg_fill, stroke: visuals.bg_stroke, }, ); @@ -371,16 +411,18 @@ fn paint_default_icon( match above_or_below { AboveOrBelow::Above => { // Upward pointing triangle - painter.add(Shape::closed_line( + painter.add(Shape::convex_polygon( vec![rect.left_bottom(), rect.right_bottom(), rect.center_top()], - visuals.fg_stroke, + visuals.fg_stroke.color, + Stroke::NONE, )); } AboveOrBelow::Below => { // Downward pointing triangle - painter.add(Shape::closed_line( + painter.add(Shape::convex_polygon( vec![rect.left_top(), rect.right_top(), rect.center_bottom()], - visuals.fg_stroke, + visuals.fg_stroke.color, + Stroke::NONE, )); } } diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index 7da48cc2..dd00d4dd 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -42,18 +42,18 @@ impl Frame { } } - pub(crate) fn side_top_panel(style: &Style) -> Self { + pub fn side_top_panel(style: &Style) -> Self { Self { inner_margin: Margin::symmetric(8.0, 2.0), - fill: style.visuals.window_fill(), + fill: style.visuals.panel_fill, ..Default::default() } } - pub(crate) fn central_panel(style: &Style) -> Self { + pub fn central_panel(style: &Style) -> Self { Self { inner_margin: Margin::same(8.0), - fill: style.visuals.window_fill(), + fill: style.visuals.panel_fill, ..Default::default() } } @@ -72,7 +72,7 @@ impl Frame { pub fn menu(style: &Style) -> Self { Self { inner_margin: style.spacing.menu_margin, - rounding: style.visuals.widgets.noninteractive.rounding, + rounding: style.visuals.menu_rounding, shadow: style.visuals.popup_shadow, fill: style.visuals.window_fill(), stroke: style.visuals.window_stroke(), @@ -82,8 +82,8 @@ impl Frame { pub fn popup(style: &Style) -> Self { Self { - inner_margin: style.spacing.window_margin, - rounding: style.visuals.widgets.noninteractive.rounding, + inner_margin: style.spacing.menu_margin, + rounding: style.visuals.menu_rounding, shadow: style.visuals.popup_shadow, fill: style.visuals.window_fill(), stroke: style.visuals.window_stroke(), diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 9b74da3a..e7633e26 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -28,7 +28,7 @@ pub struct PanelState { impl PanelState { pub fn load(ctx: &Context, bar_id: Id) -> Option { - ctx.data().get_persisted(bar_id) + ctx.data_mut(|d| d.get_persisted(bar_id)) } /// The size of the panel (from previous frame). @@ -37,14 +37,14 @@ impl PanelState { } fn store(self, ctx: &Context, bar_id: Id) { - ctx.data().insert_persisted(bar_id, self); + ctx.data_mut(|d| d.insert_persisted(bar_id, self)); } } // ---------------------------------------------------------------------------- /// [`Left`](Side::Left) or [`Right`](Side::Right) -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Side { Left, Right, @@ -97,6 +97,7 @@ pub struct SidePanel { id: Id, frame: Option, resizable: bool, + show_separator_line: bool, default_width: f32, width_range: RangeInclusive, } @@ -119,6 +120,7 @@ impl SidePanel { id: id.into(), frame: None, resizable: true, + show_separator_line: true, default_width: 200.0, width_range: 96.0..=f32::INFINITY, } @@ -140,6 +142,14 @@ impl SidePanel { self } + /// Show a separator line, even when not interacting with it? + /// + /// Default: `true`. + pub fn show_separator_line(mut self, show_separator_line: bool) -> Self { + self.show_separator_line = show_separator_line; + self + } + /// The initial wrapping width of the [`SidePanel`]. pub fn default_width(mut self, default_width: f32) -> Self { self.default_width = default_width; @@ -202,6 +212,7 @@ impl SidePanel { id, frame, resizable, + show_separator_line, default_width, width_range, } = self; @@ -215,6 +226,7 @@ impl SidePanel { } width = clamp_to_range(width, width_range.clone()).at_most(available_rect.width()); side.set_rect_width(&mut panel_rect, width); + ui.ctx().check_for_id_clash(id, panel_rect, "SidePanel"); } let mut resize_hover = false; @@ -233,11 +245,12 @@ impl SidePanel { && (resize_x - pointer.x).abs() <= ui.style().interaction.resize_grab_radius_side; - let any_pressed = ui.input().pointer.any_pressed(); // avoid deadlocks - if any_pressed && ui.input().pointer.any_down() && mouse_over_resize_line { - ui.memory().set_dragged_id(resize_id); + if ui.input(|i| i.pointer.any_pressed() && i.pointer.any_down()) + && mouse_over_resize_line + { + ui.memory_mut(|mem| mem.set_dragged_id(resize_id)); } - is_resizing = ui.memory().is_being_dragged(resize_id); + is_resizing = ui.memory(|mem| mem.is_being_dragged(resize_id)); if is_resizing { let width = (pointer.x - side.side_x(panel_rect)).abs(); let width = @@ -245,12 +258,12 @@ impl SidePanel { side.set_rect_width(&mut panel_rect, width); } - let any_down = ui.input().pointer.any_down(); // avoid deadlocks - let dragging_something_else = any_down || ui.input().pointer.any_pressed(); + let dragging_something_else = + ui.input(|i| i.pointer.any_down() || i.pointer.any_pressed()); resize_hover = mouse_over_resize_line && !dragging_something_else; if resize_hover || is_resizing { - ui.output().cursor_icon = CursorIcon::ResizeHorizontal; + ui.ctx().set_cursor_icon(CursorIcon::ResizeHorizontal); } } } @@ -284,15 +297,20 @@ impl SidePanel { { let stroke = if is_resizing { - ui.style().visuals.widgets.active.bg_stroke + ui.style().visuals.widgets.active.fg_stroke // highly visible } else if resize_hover { - ui.style().visuals.widgets.hovered.bg_stroke - } else { + ui.style().visuals.widgets.hovered.fg_stroke // highly visible + } else if show_separator_line { // TOOD(emilk): distinguish resizable from non-resizable - ui.style().visuals.widgets.noninteractive.bg_stroke + ui.style().visuals.widgets.noninteractive.bg_stroke // dim + } else { + Stroke::NONE }; // TODO(emilk): draw line on top of all panels in this ui when https://github.com/emilk/egui/issues/1516 is done - let resize_x = side.opposite().side_x(rect); + // In the meantime: nudge the line so its inside the panel, so it won't be covered by neighboring panel + // (hence the shrink). + let resize_x = side.opposite().side_x(rect.shrink(1.0)); + let resize_x = ui.painter().round_to_pixel(resize_x); ui.painter().vline(resize_x, rect.y_range(), stroke); } @@ -317,19 +335,19 @@ impl SidePanel { let layer_id = LayerId::background(); let side = self.side; let available_rect = ctx.available_rect(); - let clip_rect = ctx.input().screen_rect(); + let clip_rect = ctx.screen_rect(); let mut panel_ui = Ui::new(ctx.clone(), layer_id, self.id, available_rect, clip_rect); let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); let rect = inner_response.response.rect; match side { - Side::Left => ctx - .frame_state() - .allocate_left_panel(Rect::from_min_max(available_rect.min, rect.max)), - Side::Right => ctx - .frame_state() - .allocate_right_panel(Rect::from_min_max(rect.min, available_rect.max)), + Side::Left => ctx.frame_state_mut(|state| { + state.allocate_left_panel(Rect::from_min_max(available_rect.min, rect.max)); + }), + Side::Right => ctx.frame_state_mut(|state| { + state.allocate_right_panel(Rect::from_min_max(rect.min, available_rect.max)); + }), } inner_response } @@ -348,6 +366,8 @@ impl SidePanel { None } else if how_expanded < 1.0 { // Show a fake panel in this in-between animation state: + // TODO(emilk): move the panel out-of-screen instead of changing its width. + // Then we can actually paint it as it animates. let expanded_width = PanelState::load(ctx, self.id) .map_or(self.default_width, |state| state.rect.width()); let fake_width = how_expanded * expanded_width; @@ -381,6 +401,8 @@ impl SidePanel { None } else if how_expanded < 1.0 { // Show a fake panel in this in-between animation state: + // TODO(emilk): move the panel out-of-screen instead of changing its width. + // Then we can actually paint it as it animates. let expanded_width = PanelState::load(ui.ctx(), self.id) .map_or(self.default_width, |state| state.rect.width()); let fake_width = how_expanded * expanded_width; @@ -467,7 +489,7 @@ impl SidePanel { // ---------------------------------------------------------------------------- /// [`Top`](TopBottomSide::Top) or [`Bottom`](TopBottomSide::Bottom) -#[derive(Clone, Copy, Debug, PartialEq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TopBottomSide { Top, Bottom, @@ -520,6 +542,7 @@ pub struct TopBottomPanel { id: Id, frame: Option, resizable: bool, + show_separator_line: bool, default_height: Option, height_range: RangeInclusive, } @@ -542,6 +565,7 @@ impl TopBottomPanel { id: id.into(), frame: None, resizable: false, + show_separator_line: true, default_height: None, height_range: 20.0..=f32::INFINITY, } @@ -563,6 +587,14 @@ impl TopBottomPanel { self } + /// Show a separator line, even when not interacting with it? + /// + /// Default: `true`. + pub fn show_separator_line(mut self, show_separator_line: bool) -> Self { + self.show_separator_line = show_separator_line; + self + } + /// The initial height of the [`SidePanel`]. /// Defaults to [`style::Spacing::interact_size`].y. pub fn default_height(mut self, default_height: f32) -> Self { @@ -628,6 +660,7 @@ impl TopBottomPanel { id, frame, resizable, + show_separator_line, default_height, height_range, } = self; @@ -642,13 +675,15 @@ impl TopBottomPanel { }; height = clamp_to_range(height, height_range.clone()).at_most(available_rect.height()); side.set_rect_height(&mut panel_rect, height); + ui.ctx() + .check_for_id_clash(id, panel_rect, "TopBottomPanel"); } let mut resize_hover = false; let mut is_resizing = false; if resizable { let resize_id = id.with("__resize"); - let latest_pos = ui.input().pointer.latest_pos(); + let latest_pos = ui.input(|i| i.pointer.latest_pos()); if let Some(pointer) = latest_pos { let we_are_on_top = ui .ctx() @@ -661,13 +696,12 @@ impl TopBottomPanel { && (resize_y - pointer.y).abs() <= ui.style().interaction.resize_grab_radius_side; - if ui.input().pointer.any_pressed() - && ui.input().pointer.any_down() + if ui.input(|i| i.pointer.any_pressed() && i.pointer.any_down()) && mouse_over_resize_line { - ui.memory().interaction.drag_id = Some(resize_id); + ui.memory_mut(|mem| mem.interaction.drag_id = Some(resize_id)); } - is_resizing = ui.memory().interaction.drag_id == Some(resize_id); + is_resizing = ui.memory(|mem| mem.interaction.drag_id == Some(resize_id)); if is_resizing { let height = (pointer.y - side.side_y(panel_rect)).abs(); let height = clamp_to_range(height, height_range.clone()) @@ -675,12 +709,12 @@ impl TopBottomPanel { side.set_rect_height(&mut panel_rect, height); } - let any_down = ui.input().pointer.any_down(); // avoid deadlocks - let dragging_something_else = any_down || ui.input().pointer.any_pressed(); + let dragging_something_else = + ui.input(|i| i.pointer.any_down() || i.pointer.any_pressed()); resize_hover = mouse_over_resize_line && !dragging_something_else; if resize_hover || is_resizing { - ui.output().cursor_icon = CursorIcon::ResizeVertical; + ui.ctx().set_cursor_icon(CursorIcon::ResizeVertical); } } } @@ -714,15 +748,20 @@ impl TopBottomPanel { { let stroke = if is_resizing { - ui.style().visuals.widgets.active.bg_stroke + ui.style().visuals.widgets.active.fg_stroke // highly visible } else if resize_hover { - ui.style().visuals.widgets.hovered.bg_stroke - } else { + ui.style().visuals.widgets.hovered.fg_stroke // highly visible + } else if show_separator_line { // TOOD(emilk): distinguish resizable from non-resizable - ui.style().visuals.widgets.noninteractive.bg_stroke + ui.style().visuals.widgets.noninteractive.bg_stroke // dim + } else { + Stroke::NONE }; // TODO(emilk): draw line on top of all panels in this ui when https://github.com/emilk/egui/issues/1516 is done - let resize_y = side.opposite().side_y(rect); + // In the meantime: nudge the line so its inside the panel, so it won't be covered by neighboring panel + // (hence the shrink). + let resize_y = side.opposite().side_y(rect.shrink(1.0)); + let resize_y = ui.painter().round_to_pixel(resize_y); ui.painter().hline(rect.x_range(), resize_y, stroke); } @@ -748,7 +787,7 @@ impl TopBottomPanel { let available_rect = ctx.available_rect(); let side = self.side; - let clip_rect = ctx.input().screen_rect(); + let clip_rect = ctx.screen_rect(); let mut panel_ui = Ui::new(ctx.clone(), layer_id, self.id, available_rect, clip_rect); let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); @@ -756,12 +795,14 @@ impl TopBottomPanel { match side { TopBottomSide::Top => { - ctx.frame_state() - .allocate_top_panel(Rect::from_min_max(available_rect.min, rect.max)); + ctx.frame_state_mut(|state| { + state.allocate_top_panel(Rect::from_min_max(available_rect.min, rect.max)); + }); } TopBottomSide::Bottom => { - ctx.frame_state() - .allocate_bottom_panel(Rect::from_min_max(rect.min, available_rect.max)); + ctx.frame_state_mut(|state| { + state.allocate_bottom_panel(Rect::from_min_max(rect.min, available_rect.max)); + }); } } @@ -782,6 +823,8 @@ impl TopBottomPanel { None } else if how_expanded < 1.0 { // Show a fake panel in this in-between animation state: + // TODO(emilk): move the panel out-of-screen instead of changing its height. + // Then we can actually paint it as it animates. let expanded_height = PanelState::load(ctx, self.id) .map(|state| state.rect.height()) .or(self.default_height) @@ -817,6 +860,8 @@ impl TopBottomPanel { None } else if how_expanded < 1.0 { // Show a fake panel in this in-between animation state: + // TODO(emilk): move the panel out-of-screen instead of changing its height. + // Then we can actually paint it as it animates. let expanded_height = PanelState::load(ui.ctx(), self.id) .map(|state| state.rect.height()) .or(self.default_height) @@ -999,14 +1044,13 @@ impl CentralPanel { let layer_id = LayerId::background(); let id = Id::new("central_panel"); - let clip_rect = ctx.input().screen_rect(); + let clip_rect = ctx.screen_rect(); let mut panel_ui = Ui::new(ctx.clone(), layer_id, id, available_rect, clip_rect); let inner_response = self.show_inside_dyn(&mut panel_ui, add_contents); // Only inform ctx about what we actually used, so we can shrink the native window to fit. - ctx.frame_state() - .allocate_central_panel(inner_response.response.rect); + ctx.frame_state_mut(|state| state.allocate_central_panel(inner_response.response.rect)); inner_response } diff --git a/crates/egui/src/containers/popup.rs b/crates/egui/src/containers/popup.rs index 3ae49479..adb31ce6 100644 --- a/crates/egui/src/containers/popup.rs +++ b/crates/egui/src/containers/popup.rs @@ -13,16 +13,16 @@ pub(crate) struct TooltipState { impl TooltipState { pub fn load(ctx: &Context) -> Option { - ctx.data().get_temp(Id::null()) + ctx.data_mut(|d| d.get_temp(Id::null())) } fn store(self, ctx: &Context) { - ctx.data().insert_temp(Id::null(), self); + ctx.data_mut(|d| d.insert_temp(Id::null(), self)); } fn individual_tooltip_size(&self, common_id: Id, index: usize) -> Option { if self.last_common_id == Some(common_id) { - Some(self.individual_ids_and_sizes.get(&index).cloned()?.1) + Some(self.individual_ids_and_sizes.get(&index)?.1) } else { None } @@ -95,9 +95,7 @@ pub fn show_tooltip_at_pointer( add_contents: impl FnOnce(&mut Ui) -> R, ) -> Option { let suggested_pos = ctx - .input() - .pointer - .hover_pos() + .input(|i| i.pointer.hover_pos()) .map(|pointer_pos| pointer_pos + vec2(16.0, 16.0)); show_tooltip_at(ctx, id, suggested_pos, add_contents) } @@ -112,7 +110,7 @@ pub fn show_tooltip_for( add_contents: impl FnOnce(&mut Ui) -> R, ) -> Option { let expanded_rect = rect.expand2(vec2(2.0, 4.0)); - let (above, position) = if ctx.input().any_touches() { + let (above, position) = if ctx.input(|i| i.any_touches()) { (true, expanded_rect.left_top()) } else { (false, expanded_rect.left_bottom()) @@ -159,8 +157,7 @@ fn show_tooltip_at_avoid_dyn<'c, R>( // if there are multiple tooltips open they should use the same common_id for the `tooltip_size` caching to work. let mut frame_state = - ctx.frame_state() - .tooltip_state + ctx.frame_state(|fs| fs.tooltip_state) .unwrap_or(crate::frame_state::TooltipFrameState { common_id: individual_id, rect: Rect::NOTHING, @@ -176,7 +173,7 @@ fn show_tooltip_at_avoid_dyn<'c, R>( } } else if let Some(position) = suggested_position { position - } else if ctx.memory().everything_is_visible() { + } else if ctx.memory(|mem| mem.everything_is_visible()) { Pos2::ZERO } else { return None; // No good place for a tooltip :( @@ -191,7 +188,7 @@ fn show_tooltip_at_avoid_dyn<'c, R>( position.y -= expected_size.y; } - position = position.at_most(ctx.input().screen_rect().max - expected_size); + position = position.at_most(ctx.screen_rect().max - expected_size); // check if we intersect the avoid_rect { @@ -209,7 +206,7 @@ fn show_tooltip_at_avoid_dyn<'c, R>( } } - let position = position.at_least(ctx.input().screen_rect().min); + let position = position.at_least(ctx.screen_rect().min); let area_id = frame_state.common_id.with(frame_state.count); @@ -226,7 +223,7 @@ fn show_tooltip_at_avoid_dyn<'c, R>( frame_state.count += 1; frame_state.rect = frame_state.rect.union(response.rect); - ctx.frame_state().tooltip_state = Some(frame_state); + ctx.frame_state_mut(|fs| fs.tooltip_state = Some(frame_state)); Some(inner) } @@ -283,7 +280,7 @@ pub fn was_tooltip_open_last_frame(ctx: &Context, tooltip_id: Id) -> bool { if *individual_id == tooltip_id { let area_id = common_id.with(count); let layer_id = LayerId::new(Order::Tooltip, area_id); - if ctx.memory().areas.visible_last_frame(&layer_id) { + if ctx.memory(|mem| mem.areas.visible_last_frame(&layer_id)) { return true; } } @@ -314,6 +311,8 @@ pub fn popup_below_widget( /// /// Useful for drop-down menus (combo boxes) or suggestion menus under text fields. /// +/// The opened popup will have the same width as the parent. +/// /// You must open the popup with [`Memory::open_popup`] or [`Memory::toggle_popup`]. /// /// Returns `None` if the popup is not open. @@ -323,7 +322,7 @@ pub fn popup_below_widget( /// let response = ui.button("Open popup"); /// let popup_id = ui.make_persistent_id("my_unique_id"); /// if response.clicked() { -/// ui.memory().toggle_popup(popup_id); +/// ui.memory_mut(|mem| mem.toggle_popup(popup_id)); /// } /// let below = egui::AboveOrBelow::Below; /// egui::popup::popup_above_or_below_widget(ui, popup_id, &response, below, |ui| { @@ -340,7 +339,7 @@ pub fn popup_above_or_below_widget( above_or_below: AboveOrBelow, add_contents: impl FnOnce(&mut Ui) -> R, ) -> Option { - if ui.memory().is_popup_open(popup_id) { + if ui.memory(|mem| mem.is_popup_open(popup_id)) { let (pos, pivot) = match above_or_below { AboveOrBelow::Above => (widget_response.rect.left_top(), Align2::LEFT_BOTTOM), AboveOrBelow::Below => (widget_response.rect.left_bottom(), Align2::LEFT_TOP), @@ -368,8 +367,8 @@ pub fn popup_above_or_below_widget( }) .inner; - if ui.input().key_pressed(Key::Escape) || widget_response.clicked_elsewhere() { - ui.memory().close_popup(); + if ui.input(|i| i.key_pressed(Key::Escape)) || widget_response.clicked_elsewhere() { + ui.memory_mut(|mem| mem.close_popup()); } Some(inner) } else { diff --git a/crates/egui/src/containers/resize.rs b/crates/egui/src/containers/resize.rs index 844fc758..befb51a1 100644 --- a/crates/egui/src/containers/resize.rs +++ b/crates/egui/src/containers/resize.rs @@ -18,11 +18,11 @@ pub(crate) struct State { impl State { pub fn load(ctx: &Context, id: Id) -> Option { - ctx.data().get_persisted(id) + ctx.data_mut(|d| d.get_persisted(id)) } pub fn store(self, ctx: &Context, id: Id) { - ctx.data().insert_persisted(id, self); + ctx.data_mut(|d| d.insert_persisted(id, self)); } } @@ -180,7 +180,7 @@ impl Resize { .at_least(self.min_size) .at_most(self.max_size) .at_most( - ui.input().screen_rect().size() - ui.spacing().window_margin.sum(), // hack for windows + ui.ctx().screen_rect().size() - ui.spacing().window_margin.sum(), // hack for windows ); State { @@ -305,7 +305,7 @@ impl Resize { paint_resize_corner(ui, &corner_response); if corner_response.hovered() || corner_response.dragged() { - ui.ctx().output().cursor_icon = CursorIcon::ResizeNwSe; + ui.ctx().set_cursor_icon(CursorIcon::ResizeNwSe); } } diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 64345cbe..6d929b28 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -48,11 +48,11 @@ impl Default for State { impl State { pub fn load(ctx: &Context, id: Id) -> Option { - ctx.data().get_persisted(id) + ctx.data_mut(|d| d.get_persisted(id)) } pub fn store(self, ctx: &Context, id: Id) { - ctx.data().insert_persisted(id, self); + ctx.data_mut(|d| d.insert_persisted(id, self)); } } @@ -66,6 +66,10 @@ pub struct ScrollAreaOutput { /// The current state of the scroll area. pub state: State, + /// The size of the content. If this is larger than [`Self::inner_rect`], + /// then there was need for scrolling. + pub content_size: Vec2, + /// Where on the screen the content is (excludes scroll bars). pub inner_rect: Rect, } @@ -93,8 +97,10 @@ pub struct ScrollArea { id_source: Option, offset_x: Option, offset_y: Option, + /// If false, we ignore scroll events. scrolling_enabled: bool, + drag_to_scroll: bool, /// If true for vertical or horizontal the scroll wheel will stick to the /// end position until user manually changes position. It will become true @@ -137,6 +143,7 @@ impl ScrollArea { offset_x: None, offset_y: None, scrolling_enabled: true, + drag_to_scroll: true, stick_to_end: [false; 2], } } @@ -198,6 +205,8 @@ impl ScrollArea { /// Set the horizontal and vertical scroll offset position. /// + /// Positive offset means scrolling down/right. + /// /// See also: [`Self::vertical_scroll_offset`], [`Self::horizontal_scroll_offset`], /// [`Ui::scroll_to_cursor`](crate::ui::Ui::scroll_to_cursor) and /// [`Response::scroll_to_me`](crate::Response::scroll_to_me) @@ -209,6 +218,8 @@ impl ScrollArea { /// Set the vertical scroll offset position. /// + /// Positive offset means scrolling down. + /// /// See also: [`Self::scroll_offset`], [`Ui::scroll_to_cursor`](crate::ui::Ui::scroll_to_cursor) and /// [`Response::scroll_to_me`](crate::Response::scroll_to_me) pub fn vertical_scroll_offset(mut self, offset: f32) -> Self { @@ -218,6 +229,8 @@ impl ScrollArea { /// Set the horizontal scroll offset position. /// + /// Positive offset means scrolling right. + /// /// See also: [`Self::scroll_offset`], [`Ui::scroll_to_cursor`](crate::ui::Ui::scroll_to_cursor) and /// [`Response::scroll_to_me`](crate::Response::scroll_to_me) pub fn horizontal_scroll_offset(mut self, offset: f32) -> Self { @@ -243,12 +256,13 @@ impl ScrollArea { self } - /// Control the scrolling behavior - /// If `true` (default), the scroll area will respond to user scrolling - /// If `false`, the scroll area will not respond to user scrolling + /// Control the scrolling behavior. + /// + /// * If `true` (default), the scroll area will respond to user scrolling. + /// * If `false`, the scroll area will not respond to user scrolling. /// /// This can be used, for example, to optionally freeze scrolling while the user - /// is inputing text in a [`TextEdit`] widget contained within the scroll area. + /// is typing text in a [`TextEdit`] widget contained within the scroll area. /// /// This controls both scrolling directions. pub fn enable_scrolling(mut self, enable: bool) -> Self { @@ -256,10 +270,22 @@ impl ScrollArea { self } + /// Can the user drag the scroll area to scroll? + /// + /// This is useful for touch screens. + /// + /// If `true`, the [`ScrollArea`] will sense drags. + /// + /// Default: `true`. + pub fn drag_to_scroll(mut self, drag_to_scroll: bool) -> Self { + self.drag_to_scroll = drag_to_scroll; + self + } + /// For each axis, should the containing area shrink if the content is small? /// - /// If true, egui will add blank space outside the scroll area. - /// If false, egui will add blank space inside the scroll area. + /// * If `true`, egui will add blank space outside the scroll area. + /// * If `false`, egui will add blank space inside the scroll area. /// /// Default: `[true; 2]`. pub fn auto_shrink(mut self, auto_shrink: [bool; 2]) -> Self { @@ -325,6 +351,7 @@ impl ScrollArea { offset_x, offset_y, scrolling_enabled, + drag_to_scroll, stick_to_end, } = self; @@ -399,19 +426,40 @@ impl ScrollArea { let content_max_rect = Rect::from_min_size(inner_rect.min - state.offset, content_max_size); let mut content_ui = ui.child_ui(content_max_rect, *ui.layout()); - let mut content_clip_rect = inner_rect.expand(ui.visuals().clip_rect_margin); - content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); - // Nice handling of forced resizing beyond the possible: - for d in 0..2 { - if !has_bar[d] { - content_clip_rect.max[d] = ui.clip_rect().max[d] - current_bar_use[d]; + + { + // Clip the content, but only when we really need to: + let clip_rect_margin = ui.visuals().clip_rect_margin; + let scroll_bar_inner_margin = ui.spacing().scroll_bar_inner_margin; + let mut content_clip_rect = ui.clip_rect(); + for d in 0..2 { + if has_bar[d] { + if state.content_is_too_large[d] { + content_clip_rect.min[d] = inner_rect.min[d] - clip_rect_margin; + content_clip_rect.max[d] = inner_rect.max[d] + clip_rect_margin; + } + + if state.show_scroll[d] { + // Make sure content doesn't cover scroll bars + let tiny_gap = 1.0; + content_clip_rect.max[1 - d] = + inner_rect.max[1 - d] + scroll_bar_inner_margin - tiny_gap; + } + } else { + // Nice handling of forced resizing beyond the possible: + content_clip_rect.max[d] = ui.clip_rect().max[d] - current_bar_use[d]; + } } + // Make sure we din't accidentally expand the clip rect + content_clip_rect = content_clip_rect.intersect(ui.clip_rect()); + content_ui.set_clip_rect(content_clip_rect); } - content_ui.set_clip_rect(content_clip_rect); let viewport = Rect::from_min_size(Pos2::ZERO + state.offset, inner_size); - if scrolling_enabled && (state.content_is_too_large[0] || state.content_is_too_large[1]) { + if (scrolling_enabled && drag_to_scroll) + && (state.content_is_too_large[0] || state.content_is_too_large[1]) + { // Drag contents to scroll (for touch screens mostly). // We must do this BEFORE adding content to the `ScrollArea`, // or we will steal input from the widgets we contain. @@ -420,8 +468,10 @@ impl ScrollArea { if content_response.dragged() { for d in 0..2 { if has_bar[d] { - state.offset[d] -= ui.input().pointer.delta()[d]; - state.vel[d] = ui.input().pointer.velocity()[d]; + ui.input(|input| { + state.offset[d] -= input.pointer.delta()[d]; + state.vel[d] = input.pointer.velocity()[d]; + }); state.scroll_stuck_to_end[d] = false; } else { state.vel[d] = 0.0; @@ -430,7 +480,7 @@ impl ScrollArea { } else { let stop_speed = 20.0; // Pixels per second. let friction_coeff = 1000.0; // Pixels per second squared. - let dt = ui.input().unstable_dt; + let dt = ui.input(|i| i.unstable_dt); let friction = friction_coeff * dt; if friction > state.vel.length() || state.vel.length() < stop_speed { @@ -541,18 +591,20 @@ impl ScrollArea { let id = prepared.id; let inner_rect = prepared.inner_rect; let inner = add_contents(&mut prepared.content_ui, prepared.viewport); - let state = prepared.end(ui); + let (content_size, state) = prepared.end(ui); ScrollAreaOutput { inner, id, state, + content_size, inner_rect, } } } impl Prepared { - fn end(self, ui: &mut Ui) -> State { + /// Returns content size and state + fn end(self, ui: &mut Ui) -> (Vec2, State) { let Prepared { id, mut state, @@ -572,7 +624,9 @@ impl Prepared { for d in 0..2 { if has_bar[d] { // We take the scroll target so only this ScrollArea will use it: - let scroll_target = content_ui.ctx().frame_state().scroll_target[d].take(); + let scroll_target = content_ui + .ctx() + .frame_state_mut(|state| state.scroll_target[d].take()); if let Some((scroll, align)) = scroll_target { let min = content_ui.min_rect().min[d]; let clip_rect = content_ui.clip_rect(); @@ -623,23 +677,7 @@ impl Prepared { }; } - let mut inner_rect = Rect::from_min_size(inner_rect.min, inner_size); - - // The window that egui sits in can't be expanded by egui, so we need to respect it: - for d in 0..2 { - if !has_bar[d] { - // HACK for when we have a vertical-only scroll area in a top level panel, - // and that panel is not wide enough for the contents. - // This code ensures we still see the scroll bar! - let max = ui.input().screen_rect().max[d] - - current_bar_use[d] - - ui.spacing().item_spacing[d]; - inner_rect.max[d] = inner_rect.max[d].at_most(max); - // TODO(emilk): maybe auto-enable horizontal/vertical scrolling if this limit is reached - } - } - - inner_rect + Rect::from_min_size(inner_rect.min, inner_size) }; let outer_rect = Rect::from_min_size(inner_rect.min, inner_rect.size() + current_bar_use); @@ -653,8 +691,7 @@ impl Prepared { if scrolling_enabled && ui.rect_contains_pointer(outer_rect) { for d in 0..2 { if has_bar[d] { - let mut frame_state = ui.ctx().frame_state(); - let scroll_delta = frame_state.scroll_delta; + let scroll_delta = ui.ctx().frame_state(|fs| fs.scroll_delta); let scrolling_up = state.offset[d] > 0.0 && scroll_delta[d] > 0.0; let scrolling_down = state.offset[d] < max_offset[d] && scroll_delta[d] < 0.0; @@ -662,7 +699,7 @@ impl Prepared { if scrolling_up || scrolling_down { state.offset[d] -= scroll_delta[d]; // Clear scroll delta so no parent scroll will use it. - frame_state.scroll_delta[d] = 0.0; + ui.ctx().frame_state_mut(|fs| fs.scroll_delta[d] = 0.0); state.scroll_stuck_to_end[d] = false; } } @@ -691,13 +728,29 @@ impl Prepared { continue; } - // margin between contents and scroll bar - let margin = animation_t * ui.spacing().item_spacing.x; - let min_cross = inner_rect.max[1 - d] + margin; // left of vertical scroll (d == 1) - let max_cross = outer_rect.max[1 - d]; // right of vertical scroll (d == 1) + // margin on either side of the scroll bar + let inner_margin = animation_t * ui.spacing().scroll_bar_inner_margin; + let outer_margin = animation_t * ui.spacing().scroll_bar_outer_margin; + let mut min_cross = inner_rect.max[1 - d] + inner_margin; // left of vertical scroll (d == 1) + let mut max_cross = outer_rect.max[1 - d] - outer_margin; // right of vertical scroll (d == 1) let min_main = inner_rect.min[d]; // top of vertical scroll (d == 1) let max_main = inner_rect.max[d]; // bottom of vertical scroll (d == 1) + if ui.clip_rect().max[1 - d] < max_cross + outer_margin { + // Move the scrollbar so it is visible. This is needed in some cases. + // For instance: + // * When we have a vertical-only scroll area in a top level panel, + // and that panel is not wide enough for the contents. + // * When one ScrollArea is nested inside another, and the outer + // is scrolled so that the scroll-bars of the inner ScrollArea (us) + // is outside the clip rectangle. + // Really this should use the tighter clip_rect that ignores clip_rect_margin, but we don't store that. + // clip_rect_margin is quite a hack. It would be nice to get rid of it. + let width = max_cross - min_cross; + max_cross = ui.clip_rect().max[1 - d] - outer_margin; + min_cross = max_cross - width; + } + let outer_scroll_rect = if d == 0 { Rect::from_min_max( pos2(inner_rect.left(), min_cross), @@ -788,7 +841,7 @@ impl Prepared { ), ) }; - let min_handle_size = ui.spacing().scroll_bar_width; + let min_handle_size = ui.spacing().scroll_handle_min_length; if handle_rect.size()[d] < min_handle_size { handle_rect = Rect::from_center_size( handle_rect.center(), @@ -847,11 +900,13 @@ impl Prepared { state.store(ui.ctx(), id); - state + (content_size, state) } } /// Width of a vertical scrollbar, or height of a horizontal scroll bar fn max_scroll_bar_width_with_margin(ui: &Ui) -> f32 { - ui.spacing().item_spacing.x + ui.spacing().scroll_bar_width + ui.spacing().scroll_bar_inner_margin + + ui.spacing().scroll_bar_width + + ui.spacing().scroll_bar_outer_margin } diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 9d7032c5..a236a5e3 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -10,7 +10,7 @@ use super::*; /// /// You can customize: /// * title -/// * default, minimum, maximum and/or fixed size +/// * default, minimum, maximum and/or fixed size, collapsed/expanded /// * if the window has a scroll area (off by default) /// * if the window can be collapsed (minimized) to just the title bar (yes, by default) /// * if there should be a close button (none by default) @@ -30,6 +30,7 @@ pub struct Window<'open> { resize: Resize, scroll: ScrollArea, collapsible: bool, + default_open: bool, with_title_bar: bool, } @@ -51,6 +52,7 @@ impl<'open> Window<'open> { .default_size([340.0, 420.0]), // Default inner size of a window scroll: ScrollArea::neither(), collapsible: true, + default_open: true, with_title_bar: true, } } @@ -77,6 +79,18 @@ impl<'open> Window<'open> { self } + /// If `false` the window will be non-interactive. + pub fn interactable(mut self, interactable: bool) -> Self { + self.area = self.area.interactable(interactable); + self + } + + /// If `false` the window will be immovable. + pub fn movable(mut self, movable: bool) -> Self { + self.area = self.area.movable(movable); + self + } + /// Usage: `Window::new(โ€ฆ).mutate(|w| w.resize = w.resize.auto_expand_width(true))` // TODO(emilk): I'm not sure this is a good interface for this. pub fn mutate(mut self, mutate: impl Fn(&mut Self)) -> Self { @@ -162,6 +176,12 @@ impl<'open> Window<'open> { self } + /// Set initial collapsed state of the window + pub fn default_open(mut self, default_open: bool) -> Self { + self.default_open = default_open; + self + } + /// Set initial size of the window. pub fn default_size(mut self, default_size: impl Into) -> Self { self.resize = self.resize.default_size(default_size); @@ -275,12 +295,14 @@ impl<'open> Window<'open> { resize, scroll, collapsible, + default_open, with_title_bar, } = self; let frame = frame.unwrap_or_else(|| Frame::window(&ctx.style())); - let is_open = !matches!(open, Some(false)) || ctx.memory().everything_is_visible(); + let is_explicitly_closed = matches!(open, Some(false)); + let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible()); area.show_open_close_animation(ctx, &frame, is_open); if !is_open { @@ -291,7 +313,7 @@ impl<'open> Window<'open> { let area_layer_id = area.layer(); let resize_id = area_id.with("resize"); let mut collapsing = - CollapsingState::load_with_default_open(ctx, area_id.with("collapsing"), true); + CollapsingState::load_with_default_open(ctx, area_id.with("collapsing"), default_open); let is_collapsed = with_title_bar && !collapsing.is_open(); let possible = PossibleInteractions::new(&area, &resize, is_collapsed); @@ -318,7 +340,7 @@ impl<'open> Window<'open> { // Calculate roughly how much larger the window size is compared to the inner rect let title_bar_height = if with_title_bar { let style = ctx.style(); - title.font_height(&ctx.fonts(), &style) + title_content_spacing + ctx.fonts(|f| title.font_height(f, &style)) + title_content_spacing } else { 0.0 }; @@ -404,7 +426,7 @@ impl<'open> Window<'open> { ctx.style().visuals.widgets.active, ); } else if let Some(hover_interaction) = hover_interaction { - if ctx.input().pointer.has_pointer() { + if ctx.input(|i| i.pointer.has_pointer()) { paint_frame_interaction( &mut area_content_ui, outer_rect, @@ -499,13 +521,13 @@ pub(crate) struct WindowInteraction { impl WindowInteraction { pub fn set_cursor(&self, ctx: &Context) { if (self.left && self.top) || (self.right && self.bottom) { - ctx.output().cursor_icon = CursorIcon::ResizeNwSe; + ctx.set_cursor_icon(CursorIcon::ResizeNwSe); } else if (self.right && self.top) || (self.left && self.bottom) { - ctx.output().cursor_icon = CursorIcon::ResizeNeSw; + ctx.set_cursor_icon(CursorIcon::ResizeNeSw); } else if self.left || self.right { - ctx.output().cursor_icon = CursorIcon::ResizeHorizontal; + ctx.set_cursor_icon(CursorIcon::ResizeHorizontal); } else if self.bottom || self.top { - ctx.output().cursor_icon = CursorIcon::ResizeVertical; + ctx.set_cursor_icon(CursorIcon::ResizeVertical); } } @@ -537,7 +559,7 @@ fn interact( } } - ctx.memory().areas.move_to_top(area_layer_id); + ctx.memory_mut(|mem| mem.areas.move_to_top(area_layer_id)); Some(window_interaction) } @@ -545,11 +567,11 @@ fn move_and_resize_window(ctx: &Context, window_interaction: &WindowInteraction) window_interaction.set_cursor(ctx); // Only move/resize windows with primary mouse button: - if !ctx.input().pointer.primary_down() { + if !ctx.input(|i| i.pointer.primary_down()) { return None; } - let pointer_pos = ctx.input().pointer.interact_pos()?; + let pointer_pos = ctx.input(|i| i.pointer.interact_pos())?; let mut rect = window_interaction.start_rect; // prevent drift if window_interaction.is_resize() { @@ -571,8 +593,8 @@ fn move_and_resize_window(ctx: &Context, window_interaction: &WindowInteraction) // but we want anything interactive in the window (e.g. slider) to steal // the drag from us. It is therefor important not to move the window the first frame, // but instead let other widgets to the steal. HACK. - if !ctx.input().pointer.any_pressed() { - let press_origin = ctx.input().pointer.press_origin()?; + if !ctx.input(|i| i.pointer.any_pressed()) { + let press_origin = ctx.input(|i| i.pointer.press_origin())?; let delta = pointer_pos - press_origin; rect = rect.translate(delta); } @@ -590,30 +612,31 @@ fn window_interaction( rect: Rect, ) -> Option { { - let drag_id = ctx.memory().interaction.drag_id; + let drag_id = ctx.memory(|mem| mem.interaction.drag_id); if drag_id.is_some() && drag_id != Some(id) { return None; } } - let mut window_interaction = { ctx.memory().window_interaction }; + let mut window_interaction = ctx.memory(|mem| mem.window_interaction); if window_interaction.is_none() { if let Some(hover_window_interaction) = resize_hover(ctx, possible, area_layer_id, rect) { hover_window_interaction.set_cursor(ctx); - let any_pressed = ctx.input().pointer.any_pressed(); // avoid deadlocks - if any_pressed && ctx.input().pointer.primary_down() { - ctx.memory().interaction.drag_id = Some(id); - ctx.memory().interaction.drag_is_window = true; - window_interaction = Some(hover_window_interaction); - ctx.memory().window_interaction = window_interaction; + if ctx.input(|i| i.pointer.any_pressed() && i.pointer.primary_down()) { + ctx.memory_mut(|mem| { + mem.interaction.drag_id = Some(id); + mem.interaction.drag_is_window = true; + window_interaction = Some(hover_window_interaction); + mem.window_interaction = window_interaction; + }); } } } if let Some(window_interaction) = window_interaction { - let is_active = ctx.memory().interaction.drag_id == Some(id); + let is_active = ctx.memory_mut(|mem| mem.interaction.drag_id == Some(id)); if is_active && window_interaction.area_layer_id == area_layer_id { return Some(window_interaction); @@ -629,10 +652,9 @@ fn resize_hover( area_layer_id: LayerId, rect: Rect, ) -> Option { - let pointer = ctx.input().pointer.interact_pos()?; + let pointer = ctx.input(|i| i.pointer.interact_pos())?; - let any_down = ctx.input().pointer.any_down(); // avoid deadlocks - if any_down && !ctx.input().pointer.any_pressed() { + if ctx.input(|i| i.pointer.any_down() && !i.pointer.any_pressed()) { return None; // already dragging (something) } @@ -642,7 +664,7 @@ fn resize_hover( } } - if ctx.memory().interaction.drag_interest { + if ctx.memory(|mem| mem.interaction.drag_interest) { // Another widget will become active if we drag here return None; } @@ -804,8 +826,8 @@ fn show_title_bar( collapsible: bool, ) -> TitleBar { let inner_response = ui.horizontal(|ui| { - let height = title - .font_height(&ui.fonts(), ui.style()) + let height = ui + .fonts(|fonts| title.font_height(fonts, ui.style())) .max(ui.spacing().interact_size.y); ui.set_min_height(height); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 31455702..99cb9f05 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use crate::{ animation_manager::AnimationManager, data::output::PlatformOutput, frame_state::FrameState, input_state::*, layers::GraphicLayers, memory::Options, os::OperatingSystem, - output::FullOutput, TextureHandle, *, + output::FullOutput, util::IdTypeMap, TextureHandle, *, }; use epaint::{mutex::*, stats::*, text::Fonts, TessellationOptions, *}; @@ -67,6 +67,9 @@ struct ContextImpl { layer_rects_this_frame: ahash::HashMap>, /// Read layer_rects_prev_frame: ahash::HashMap>, + + #[cfg(feature = "accesskit")] + is_accesskit_enabled: bool, } impl ContextImpl { @@ -105,6 +108,25 @@ impl ContextImpl { interactable: true, }, ); + + #[cfg(feature = "accesskit")] + if self.is_accesskit_enabled { + use crate::frame_state::AccessKitFrameState; + let id = crate::accesskit_root_id(); + let node = Box::new(accesskit::Node { + role: accesskit::Role::Window, + transform: Some( + accesskit::kurbo::Affine::scale(self.input.pixels_per_point().into()).into(), + ), + ..Default::default() + }); + let mut nodes = IdMap::default(); + nodes.insert(id, node); + self.frame_state.accesskit_state = Some(AccessKitFrameState { + nodes, + parent_stack: vec![id], + }); + } } /// Load fonts unless already loaded. @@ -132,6 +154,19 @@ impl ContextImpl { } } } + + #[cfg(feature = "accesskit")] + fn accesskit_node(&mut self, id: Id) -> &mut accesskit::Node { + let state = self.frame_state.accesskit_state.as_mut().unwrap(); + let nodes = &mut state.nodes; + if let std::collections::hash_map::Entry::Vacant(entry) = nodes.entry(id) { + entry.insert(Default::default()); + let parent_id = state.parent_stack.last().unwrap(); + let parent = nodes.get_mut(parent_id).unwrap(); + parent.children.push(id.accesskit_id()); + } + nodes.get_mut(&id).unwrap() + } } // ---------------------------------------------------------------------------- @@ -144,12 +179,26 @@ impl ContextImpl { /// [`Context`] is cheap to clone, and any clones refers to the same mutable data /// ([`Context`] uses refcounting internally). /// -/// All methods are marked `&self`; [`Context`] has interior mutability (protected by a mutex). +/// ## Locking +/// All methods are marked `&self`; [`Context`] has interior mutability protected by an [`RwLock`]. /// +/// To access parts of a `Context` you need to use some of the helper functions that take closures: /// -/// You can store +/// ``` +/// # let ctx = egui::Context::default(); +/// if ctx.input(|i| i.key_pressed(egui::Key::A)) { +/// ctx.output_mut(|o| o.copied_text = "Hello!".to_string()); +/// } +/// ``` /// -/// # Example: +/// Within such a closure you may NOT recursively lock the same [`Context`], as that can lead to a deadlock. +/// Therefore it is important that any lock of [`Context`] is short-lived. +/// +/// These are effectively transactional accesses. +/// +/// [`Ui`] has many of the same accessor functions, and the same applies there. +/// +/// ## Example: /// /// ``` no_run /// # fn handle_platform_output(_: egui::PlatformOutput) {} @@ -199,12 +248,14 @@ impl Default for Context { } impl Context { - fn read(&self) -> RwLockReadGuard<'_, ContextImpl> { - self.0.read() + // Do read-only (shared access) transaction on Context + fn read(&self, reader: impl FnOnce(&ContextImpl) -> R) -> R { + reader(&self.0.read()) } - fn write(&self) -> RwLockWriteGuard<'_, ContextImpl> { - self.0.write() + // Do read-write (exclusive access) transaction on Context + fn write(&self, writer: impl FnOnce(&mut ContextImpl) -> R) -> R { + writer(&mut self.0.write()) } /// Run the ui code for one frame. @@ -254,9 +305,150 @@ impl Context { /// // handle full_output /// ``` pub fn begin_frame(&self, new_input: RawInput) { - self.write().begin_frame_mut(new_input); + self.write(|ctx| ctx.begin_frame_mut(new_input)); + } +} + +/// ## Borrows parts of [`Context`] +/// These functions all lock the [`Context`]. +/// Please see the documentation of [`Context`] for how locking works! +impl Context { + /// Read-only access to [`InputState`]. + /// + /// Note that this locks the [`Context`]. + /// + /// ``` + /// # let mut ctx = egui::Context::default(); + /// ctx.input(|i| { + /// // โš ๏ธ Using `ctx` (even from other `Arc` reference) again here will lead to a dead-lock! + /// }); + /// + /// if let Some(pos) = ctx.input(|i| i.pointer.hover_pos()) { + /// // This is fine! + /// } + /// ``` + #[inline] + pub fn input(&self, reader: impl FnOnce(&InputState) -> R) -> R { + self.read(move |ctx| reader(&ctx.input)) } + /// Read-write access to [`InputState`]. + #[inline] + pub fn input_mut(&self, writer: impl FnOnce(&mut InputState) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.input)) + } + + /// Read-only access to [`Memory`]. + #[inline] + pub fn memory(&self, reader: impl FnOnce(&Memory) -> R) -> R { + self.read(move |ctx| reader(&ctx.memory)) + } + + /// Read-write access to [`Memory`]. + #[inline] + pub fn memory_mut(&self, writer: impl FnOnce(&mut Memory) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.memory)) + } + + /// Read-only access to [`IdTypeMap`], which stores superficial widget state. + #[inline] + pub fn data(&self, reader: impl FnOnce(&IdTypeMap) -> R) -> R { + self.read(move |ctx| reader(&ctx.memory.data)) + } + + /// Read-write access to [`IdTypeMap`], which stores superficial widget state. + #[inline] + pub fn data_mut(&self, writer: impl FnOnce(&mut IdTypeMap) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.memory.data)) + } + + /// Read-write access to [`GraphicLayers`], where painted [`crate::Shape`]s are written to. + #[inline] + pub(crate) fn graphics_mut(&self, writer: impl FnOnce(&mut GraphicLayers) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.graphics)) + } + + /// Read-only access to [`PlatformOutput`]. + /// + /// This is what egui outputs each frame. + /// + /// ``` + /// # let mut ctx = egui::Context::default(); + /// ctx.output_mut(|o| o.cursor_icon = egui::CursorIcon::Progress); + /// ``` + #[inline] + pub fn output(&self, reader: impl FnOnce(&PlatformOutput) -> R) -> R { + self.read(move |ctx| reader(&ctx.output)) + } + + /// Read-write access to [`PlatformOutput`]. + #[inline] + pub fn output_mut(&self, writer: impl FnOnce(&mut PlatformOutput) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.output)) + } + + /// Read-only access to [`FrameState`]. + #[inline] + pub(crate) fn frame_state(&self, reader: impl FnOnce(&FrameState) -> R) -> R { + self.read(move |ctx| reader(&ctx.frame_state)) + } + + /// Read-write access to [`FrameState`]. + #[inline] + pub(crate) fn frame_state_mut(&self, writer: impl FnOnce(&mut FrameState) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.frame_state)) + } + + /// Read-only access to [`Fonts`]. + /// + /// Not valid until first call to [`Context::run()`]. + /// That's because since we don't know the proper `pixels_per_point` until then. + #[inline] + pub fn fonts(&self, reader: impl FnOnce(&Fonts) -> R) -> R { + self.read(move |ctx| { + reader( + ctx.fonts + .as_ref() + .expect("No fonts available until first call to Context::run()"), + ) + }) + } + + /// Read-write access to [`Fonts`]. + #[inline] + pub fn fonts_mut(&self, writer: impl FnOnce(&mut Option) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.fonts)) + } + + /// Read-only access to [`Options`]. + #[inline] + pub fn options(&self, reader: impl FnOnce(&Options) -> R) -> R { + self.read(move |ctx| reader(&ctx.memory.options)) + } + + /// Read-write access to [`Options`]. + #[inline] + pub fn options_mut(&self, writer: impl FnOnce(&mut Options) -> R) -> R { + self.write(move |ctx| writer(&mut ctx.memory.options)) + } + + /// Read-only access to [`TessellationOptions`]. + #[inline] + pub fn tessellation_options(&self, reader: impl FnOnce(&TessellationOptions) -> R) -> R { + self.read(move |ctx| reader(&ctx.memory.options.tessellation_options)) + } + + /// Read-write access to [`TessellationOptions`]. + #[inline] + pub fn tessellation_options_mut( + &self, + writer: impl FnOnce(&mut TessellationOptions) -> R, + ) -> R { + self.write(move |ctx| writer(&mut ctx.memory.options.tessellation_options)) + } +} + +impl Context { // --------------------------------------------------------------------- /// If the given [`Id`] has been used previously the same frame at at different position, @@ -269,7 +461,7 @@ impl Context { /// The most important thing is that [`Rect::min`] is approximately correct, /// because that's where the warning will be painted. If you don't know what size to pick, just pick [`Vec2::ZERO`]. pub fn check_for_id_clash(&self, id: Id, new_rect: Rect, what: &str) { - let prev_rect = self.frame_state().used_ids.insert(id, new_rect); + let prev_rect = self.frame_state_mut(move |state| state.used_ids.insert(id, new_rect)); if let Some(prev_rect) = prev_rect { // it is ok to reuse the same ID for e.g. a frame around a widget, // or to check for interaction with the same widget twice: @@ -279,16 +471,45 @@ impl Context { return; } - let show_error = |pos: Pos2, text: String| { + let show_error = |widget_rect: Rect, text: String| { + let text = format!("๐Ÿ”ฅ {}", text); + let color = self.style().visuals.error_fg_color; let painter = self.debug_painter(); - let rect = painter.error(pos, text); + painter.rect_stroke(widget_rect, 0.0, (1.0, color)); + + let below = widget_rect.bottom() + 32.0 < self.input(|i| i.screen_rect.bottom()); + + let text_rect = if below { + painter.debug_text( + widget_rect.left_bottom() + vec2(0.0, 2.0), + Align2::LEFT_TOP, + color, + text, + ) + } else { + painter.debug_text( + widget_rect.left_top() - vec2(0.0, 2.0), + Align2::LEFT_BOTTOM, + color, + text, + ) + }; + if let Some(pointer_pos) = self.pointer_hover_pos() { - if rect.contains(pointer_pos) { + if text_rect.contains(pointer_pos) { + let tooltip_pos = if below { + text_rect.left_bottom() + vec2(2.0, 4.0) + } else { + text_rect.left_top() + vec2(2.0, -4.0) + }; + painter.error( - rect.left_bottom() + vec2(2.0, 4.0), - "ID clashes happens when things like Windows or CollapsingHeaders share names,\n\ + tooltip_pos, + format!("Widget is {} this text.\n\n\ + ID clashes happens when things like Windows or CollapsingHeaders share names,\n\ or when things like Plot and Grid:s aren't given unique id_source:s.\n\n\ Sometimes the solution is to use ui.push_id.", + if below { "above" } else { "below" }) ); } } @@ -297,19 +518,10 @@ impl Context { let id_str = id.short_debug_format(); if prev_rect.min.distance(new_rect.min) < 4.0 { - show_error( - new_rect.min, - format!("Double use of {} ID {}", what, id_str), - ); + show_error(new_rect, format!("Double use of {} ID {}", what, id_str)); } else { - show_error( - prev_rect.min, - format!("First use of {} ID {}", what, id_str), - ); - show_error( - new_rect.min, - format!("Second use of {} ID {}", what, id_str), - ); + show_error(prev_rect, format!("First use of {} ID {}", what, id_str)); + show_error(new_rect, format!("Second use of {} ID {}", what, id_str)); } } } @@ -353,46 +565,45 @@ impl Context { ); } - let mut slf = self.write(); + self.write(|ctx| { + ctx.layer_rects_this_frame + .entry(layer_id) + .or_default() + .push((id, interact_rect)); - slf.layer_rects_this_frame - .entry(layer_id) - .or_default() - .push((id, interact_rect)); - - if hovered { - let pointer_pos = slf.input.pointer.interact_pos(); - if let Some(pointer_pos) = pointer_pos { - if let Some(rects) = slf.layer_rects_prev_frame.get(&layer_id) { - for &(prev_id, prev_rect) in rects.iter().rev() { - if prev_id == id { - break; // there is no other interactive widget covering us at the pointer position. - } - if prev_rect.contains(pointer_pos) { - // Another interactive widget is covering us at the pointer position, - // so we aren't hovered. - - if slf.memory.options.style.debug.show_blocking_widget { - drop(slf); - Self::layer_painter(self, LayerId::debug()).debug_rect( - interact_rect, - Color32::GREEN, - "Covered", - ); - Self::layer_painter(self, LayerId::debug()).debug_rect( - prev_rect, - Color32::LIGHT_BLUE, - "On top", - ); + if hovered { + let pointer_pos = ctx.input.pointer.interact_pos(); + if let Some(pointer_pos) = pointer_pos { + if let Some(rects) = ctx.layer_rects_prev_frame.get(&layer_id) { + for &(prev_id, prev_rect) in rects.iter().rev() { + if prev_id == id { + break; // there is no other interactive widget covering us at the pointer position. } + if prev_rect.contains(pointer_pos) { + // Another interactive widget is covering us at the pointer position, + // so we aren't hovered. - hovered = false; - break; + if ctx.memory.options.style.debug.show_blocking_widget { + Self::layer_painter(self, LayerId::debug()).debug_rect( + interact_rect, + Color32::GREEN, + "Covered", + ); + Self::layer_painter(self, LayerId::debug()).debug_rect( + prev_rect, + Color32::LIGHT_BLUE, + "On top", + ); + } + + hovered = false; + break; + } } } } } - } + }); } self.interact_with_hovered(layer_id, id, rect, sense, enabled, hovered) @@ -410,6 +621,8 @@ impl Context { ) -> Response { let hovered = hovered && enabled; // can't even hover disabled widgets + let highlighted = self.frame_state(|fs| fs.highlight_this_frame.contains(&id)); + let mut response = Response { ctx: self.clone(), layer_id, @@ -418,6 +631,7 @@ impl Context { sense, enabled, hovered, + highlighted, clicked: Default::default(), double_clicked: Default::default(), triple_clicked: Default::default(), @@ -430,112 +644,126 @@ impl Context { if !enabled || !sense.focusable || !layer_id.allow_interaction() { // Not interested or allowed input: - self.memory().surrender_focus(id); + self.memory_mut(|mem| mem.surrender_focus(id)); return response; } self.check_for_id_clash(id, rect, "widget"); + #[cfg(feature = "accesskit")] + if sense.focusable { + // Make sure anything that can receive focus has an AccessKit node. + // TODO(mwcampbell): For nodes that are filled from widget info, + // some information is written to the node twice. + self.accesskit_node(id, |node| response.fill_accesskit_node_common(node)); + } + let clicked_elsewhere = response.clicked_elsewhere(); - let ctx_impl = &mut *self.write(); - let memory = &mut ctx_impl.memory; - let input = &mut ctx_impl.input; + self.write(|ctx| { + let memory = &mut ctx.memory; + let input = &mut ctx.input; - // We only want to focus labels if the screen reader is on. - let interested_in_focus = - sense.interactive() || sense.focusable && memory.options.screen_reader; + if sense.focusable { + memory.interested_in_focus(id); + } - if interested_in_focus { - memory.interested_in_focus(id); - } + if sense.click + && memory.has_focus(response.id) + && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter)) + { + // Space/enter works like a primary click for e.g. selected buttons + response.clicked[PointerButton::Primary as usize] = true; + } - if sense.click - && memory.has_focus(response.id) - && (input.key_pressed(Key::Space) || input.key_pressed(Key::Enter)) - { - // Space/enter works like a primary click for e.g. selected buttons - response.clicked[PointerButton::Primary as usize] = true; - } + #[cfg(feature = "accesskit")] + { + if sense.click + && input.has_accesskit_action_request(response.id, accesskit::Action::Default) + { + response.clicked[PointerButton::Primary as usize] = true; + } + } - if sense.click || sense.drag { - memory.interaction.click_interest |= hovered && sense.click; - memory.interaction.drag_interest |= hovered && sense.drag; + if sense.click || sense.drag { + memory.interaction.click_interest |= hovered && sense.click; + memory.interaction.drag_interest |= hovered && sense.drag; - response.dragged = memory.interaction.drag_id == Some(id); - response.is_pointer_button_down_on = - memory.interaction.click_id == Some(id) || response.dragged; + response.dragged = memory.interaction.drag_id == Some(id); + response.is_pointer_button_down_on = + memory.interaction.click_id == Some(id) || response.dragged; - for pointer_event in &input.pointer.pointer_events { - match pointer_event { - PointerEvent::Moved(_) => {} - PointerEvent::Pressed { .. } => { - if hovered { - if sense.click && memory.interaction.click_id.is_none() { - // potential start of a click - memory.interaction.click_id = Some(id); - response.is_pointer_button_down_on = true; - } + for pointer_event in &input.pointer.pointer_events { + match pointer_event { + PointerEvent::Moved(_) => {} + PointerEvent::Pressed { .. } => { + if hovered { + if sense.click && memory.interaction.click_id.is_none() { + // potential start of a click + memory.interaction.click_id = Some(id); + response.is_pointer_button_down_on = true; + } - // HACK: windows have low priority on dragging. - // This is so that if you drag a slider in a window, - // the slider will steal the drag away from the window. - // This is needed because we do window interaction first (to prevent frame delay), - // and then do content layout. - if sense.drag - && (memory.interaction.drag_id.is_none() - || memory.interaction.drag_is_window) - { - // potential start of a drag - memory.interaction.drag_id = Some(id); - memory.interaction.drag_is_window = false; - memory.window_interaction = None; // HACK: stop moving windows (if any) - response.is_pointer_button_down_on = true; - response.dragged = true; + // HACK: windows have low priority on dragging. + // This is so that if you drag a slider in a window, + // the slider will steal the drag away from the window. + // This is needed because we do window interaction first (to prevent frame delay), + // and then do content layout. + if sense.drag + && (memory.interaction.drag_id.is_none() + || memory.interaction.drag_is_window) + { + // potential start of a drag + memory.interaction.drag_id = Some(id); + memory.interaction.drag_is_window = false; + memory.window_interaction = None; // HACK: stop moving windows (if any) + response.is_pointer_button_down_on = true; + response.dragged = true; + } } } - } - PointerEvent::Released(click) => { - response.drag_released = response.dragged; - response.dragged = false; + PointerEvent::Released { click, button } => { + response.drag_released = response.dragged; + response.dragged = false; - if hovered && response.is_pointer_button_down_on { - if let Some(click) = click { - let clicked = hovered && response.is_pointer_button_down_on; - response.clicked[click.button as usize] = clicked; - response.double_clicked[click.button as usize] = - clicked && click.is_double(); - response.triple_clicked[click.button as usize] = - clicked && click.is_triple(); + if hovered && response.is_pointer_button_down_on { + if let Some(click) = click { + let clicked = hovered && response.is_pointer_button_down_on; + response.clicked[*button as usize] = clicked; + response.double_clicked[*button as usize] = + clicked && click.is_double(); + response.triple_clicked[*button as usize] = + clicked && click.is_triple(); + } } } } } } - } - if response.is_pointer_button_down_on { - response.interact_pointer_pos = input.pointer.interact_pos(); - } + if response.is_pointer_button_down_on { + response.interact_pointer_pos = input.pointer.interact_pos(); + } - if input.pointer.any_down() { - response.hovered &= response.is_pointer_button_down_on; // we don't hover widgets while interacting with *other* widgets - } + if input.pointer.any_down() { + response.hovered &= response.is_pointer_button_down_on; // we don't hover widgets while interacting with *other* widgets + } - if memory.has_focus(response.id) && clicked_elsewhere { - memory.surrender_focus(id); - } + if memory.has_focus(response.id) && clicked_elsewhere { + memory.surrender_focus(id); + } - if response.dragged() && !memory.has_focus(response.id) { - // e.g.: remove focus from a widget when you drag something else - memory.stop_text_input(); - } + if response.dragged() && !memory.has_focus(response.id) { + // e.g.: remove focus from a widget when you drag something else + memory.stop_text_input(); + } + }); response } /// Get a full-screen painter for a new or existing layer pub fn layer_painter(&self, layer_id: LayerId) -> Painter { - let screen_rect = self.input().screen_rect(); + let screen_rect = self.screen_rect(); Painter::new(self.clone(), layer_id, screen_rect) } @@ -544,107 +772,6 @@ impl Context { Self::layer_painter(self, LayerId::debug()) } - /// How much space is still available after panels has been added. - /// This is the "background" area, what egui doesn't cover with panels (but may cover with windows). - /// This is also the area to which windows are constrained. - pub fn available_rect(&self) -> Rect { - self.frame_state().available_rect() - } -} - -/// ## Borrows parts of [`Context`] -impl Context { - /// Stores all the egui state. - /// - /// If you want to store/restore egui, serialize this. - #[inline] - pub fn memory(&self) -> RwLockWriteGuard<'_, Memory> { - RwLockWriteGuard::map(self.write(), |c| &mut c.memory) - } - - /// Stores superficial widget state. - #[inline] - pub fn data(&self) -> RwLockWriteGuard<'_, crate::util::IdTypeMap> { - RwLockWriteGuard::map(self.write(), |c| &mut c.memory.data) - } - - #[inline] - pub(crate) fn graphics(&self) -> RwLockWriteGuard<'_, GraphicLayers> { - RwLockWriteGuard::map(self.write(), |c| &mut c.graphics) - } - - /// What egui outputs each frame. - /// - /// ``` - /// # let mut ctx = egui::Context::default(); - /// ctx.output().cursor_icon = egui::CursorIcon::Progress; - /// ``` - #[inline] - pub fn output(&self) -> RwLockWriteGuard<'_, PlatformOutput> { - RwLockWriteGuard::map(self.write(), |c| &mut c.output) - } - - #[inline] - pub(crate) fn frame_state(&self) -> RwLockWriteGuard<'_, FrameState> { - RwLockWriteGuard::map(self.write(), |c| &mut c.frame_state) - } - - /// Access the [`InputState`]. - /// - /// Note that this locks the [`Context`], so be careful with if-let bindings: - /// - /// ``` - /// # let mut ctx = egui::Context::default(); - /// if let Some(pos) = ctx.input().pointer.hover_pos() { - /// // โš ๏ธ Using `ctx` again here will lead to a dead-lock! - /// } - /// - /// if let Some(pos) = { ctx.input().pointer.hover_pos() } { - /// // This is fine! - /// } - /// - /// let pos = ctx.input().pointer.hover_pos(); - /// if let Some(pos) = pos { - /// // This is fine! - /// } - /// ``` - #[inline] - pub fn input(&self) -> RwLockReadGuard<'_, InputState> { - RwLockReadGuard::map(self.read(), |c| &c.input) - } - - #[inline] - pub fn input_mut(&self) -> RwLockWriteGuard<'_, InputState> { - RwLockWriteGuard::map(self.write(), |c| &mut c.input) - } - - /// Not valid until first call to [`Context::run()`]. - /// That's because since we don't know the proper `pixels_per_point` until then. - #[inline] - pub fn fonts(&self) -> RwLockReadGuard<'_, Fonts> { - RwLockReadGuard::map(self.read(), |c| { - c.fonts - .as_ref() - .expect("No fonts available until first call to Context::run()") - }) - } - - #[inline] - fn fonts_mut(&self) -> RwLockWriteGuard<'_, Option> { - RwLockWriteGuard::map(self.write(), |c| &mut c.fonts) - } - - #[inline] - pub fn options(&self) -> RwLockWriteGuard<'_, Options> { - RwLockWriteGuard::map(self.write(), |c| &mut c.memory.options) - } - - /// Change the options used by the tessellator. - #[inline] - pub fn tessellation_options(&self) -> RwLockWriteGuard<'_, TessellationOptions> { - RwLockWriteGuard::map(self.write(), |c| &mut c.memory.options.tessellation_options) - } - /// What operating system are we running on? /// /// When compiling natively, this is @@ -653,7 +780,7 @@ impl Context { /// For web, this can be figured out from the user-agent, /// and is done so by [`eframe`](https://github.com/emilk/egui/tree/master/crates/eframe). pub fn os(&self) -> OperatingSystem { - self.read().os + self.read(|ctx| ctx.os) } /// Set the operating system we are running on. @@ -661,7 +788,18 @@ impl Context { /// If you are writing wasm-based integration for egui you /// may want to set this based on e.g. the user-agent. pub fn set_os(&self, os: OperatingSystem) { - self.write().os = os; + self.write(|ctx| ctx.os = os); + } + + /// Set the cursor icon. + /// + /// Equivalent to: + /// ``` + /// # let ctx = egui::Context::default(); + /// ctx.output_mut(|o| o.cursor_icon = egui::CursorIcon::PointingHand); + /// ``` + pub fn set_cursor_icon(&self, cursor_icon: CursorIcon) { + self.output_mut(|o| o.cursor_icon = cursor_icon); } /// Format the given shortcut in a human-readable way (e.g. `Ctrl+Shift+X`). @@ -682,13 +820,14 @@ impl Context { } = ModifierNames::SYMBOLS; let font_id = TextStyle::Body.resolve(&self.style()); - let fonts = self.fonts(); - let mut fonts = fonts.lock(); - let font = fonts.fonts.font(&font_id); - font.has_glyphs(alt) - && font.has_glyphs(ctrl) - && font.has_glyphs(shift) - && font.has_glyphs(mac_cmd) + self.fonts(|f| { + let mut lock = f.lock(); + let font = lock.fonts.font(&font_id); + font.has_glyphs(alt) + && font.has_glyphs(ctrl) + && font.has_glyphs(shift) + && font.has_glyphs(mac_cmd) + }) }; if is_mac && can_show_symbols() { @@ -697,9 +836,7 @@ impl Context { shortcut.format(&ModifierNames::NAMES, is_mac) } } -} -impl Context { /// Call this if there is need to repaint the UI, i.e. if you are showing an animation. /// /// If this is called at least once in a frame, then there will be another frame right after this. @@ -710,14 +847,15 @@ impl Context { /// (this will work on `eframe`). pub fn request_repaint(&self) { // request two frames of repaint, just to cover some corner cases (frame delays): - let mut ctx = self.write(); - ctx.repaint_requests = 2; - if let Some(callback) = &ctx.request_repaint_callback { - if !ctx.has_requested_repaint_this_frame { - (callback)(); - ctx.has_requested_repaint_this_frame = true; + self.write(|ctx| { + ctx.repaint_requests = 2; + if let Some(callback) = &ctx.request_repaint_callback { + if !ctx.has_requested_repaint_this_frame { + (callback)(); + ctx.has_requested_repaint_this_frame = true; + } } - } + }); } /// Request repaint after the specified duration elapses in the case of no new input @@ -749,8 +887,7 @@ impl Context { /// during app idle time where we are not receiving any new input events. pub fn request_repaint_after(&self, duration: std::time::Duration) { // Maybe we can check if duration is ZERO, and call self.request_repaint()? - let mut ctx = self.write(); - ctx.repaint_after = ctx.repaint_after.min(duration); + self.write(|ctx| ctx.repaint_after = ctx.repaint_after.min(duration)); } /// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`]. @@ -760,7 +897,7 @@ impl Context { /// Note that only one callback can be set. Any new call overrides the previous callback. pub fn set_request_repaint_callback(&self, callback: impl Fn() + Send + Sync + 'static) { let callback = Box::new(callback); - self.write().request_repaint_callback = Some(callback); + self.write(|ctx| ctx.request_repaint_callback = Some(callback)); } /// Tell `egui` which fonts to use. @@ -770,19 +907,23 @@ impl Context { /// /// The new fonts will become active at the start of the next frame. pub fn set_fonts(&self, font_definitions: FontDefinitions) { - if let Some(current_fonts) = &*self.fonts_mut() { - // NOTE: this comparison is expensive since it checks TTF data for equality - if current_fonts.lock().fonts.definitions() == &font_definitions { - return; // no change - save us from reloading font textures + let update_fonts = self.fonts_mut(|fonts| { + if let Some(current_fonts) = fonts { + // NOTE: this comparison is expensive since it checks TTF data for equality + current_fonts.lock().fonts.definitions() != &font_definitions + } else { + true } - } + }); - self.memory().new_font_definitions = Some(font_definitions); + if update_fonts { + self.memory_mut(|mem| mem.new_font_definitions = Some(font_definitions)); + } } /// The [`Style`] used by all subsequent windows, panels etc. pub fn style(&self) -> Arc