From c86d0e59185d2796de4c72081f9431a7de12436a Mon Sep 17 00:00:00 2001 From: Valentin Date: Sat, 30 Nov 2024 12:56:23 +0100 Subject: [PATCH 1/8] fix accidental change of FallbackEgl to PreferEgl (#5408) I accidentally changed this in a previous commit when I meant to only change the comment above it. https://github.com/emilk/egui/pull/5392#discussion_r1859383653 --- crates/eframe/src/native/glow_integration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index db721a64697..2bd80cecbc3 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -943,7 +943,7 @@ impl GlutinWindowContext { // we might want to expose this option to users in the future. maybe using an env var or using native_options. // // The justification for FallbackEgl over PreferEgl is at https://github.com/emilk/egui/pull/2526#issuecomment-1400229576 . - .with_preference(glutin_winit::ApiPreference::PreferEgl) + .with_preference(glutin_winit::ApiPreference::FallbackEgl) .with_window_attributes(Some(egui_winit::create_winit_window_attributes( egui_ctx, event_loop, From 7e3275ca5caf27b1340b2f18e636041df387908e Mon Sep 17 00:00:00 2001 From: lucasmerlin Date: Sun, 1 Dec 2024 18:57:41 +0100 Subject: [PATCH 2/8] Fix cargo machete (#5410) * [x] I have followed the instructions in the PR template cargo machete depends on cargo-platform which seems to bumped it's msrv. Installing via --locked should fix this for now. I think it's fine to do this manually instead of using the cargo action since it's so simple and the action we used basically did the same (without --locked) --- .github/workflows/cargo_machete.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cargo_machete.yml b/.github/workflows/cargo_machete.yml index dab6725553c..2b06dfdef36 100644 --- a/.github/workflows/cargo_machete.yml +++ b/.github/workflows/cargo_machete.yml @@ -9,4 +9,4 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Machete - uses: bnjbvr/cargo-machete@main + run: cargo install cargo-machete --locked && cargo machete From 328422dc62f1396cce550780279efdb341152025 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Sun, 1 Dec 2024 18:58:35 +0100 Subject: [PATCH 3/8] Update MSRV to Rust 1.79 (#5421) Mostly to fix `cargo-machete` CI --- .github/workflows/deploy_web_demo.yml | 2 +- .github/workflows/rust.yml | 14 ++++---- Cargo.toml | 16 +++++++-- clippy.toml | 7 ++-- crates/eframe/src/epi.rs | 2 +- .../eframe/src/native/event_loop_context.rs | 2 +- crates/eframe/src/web/web_painter_wgpu.rs | 34 ++----------------- crates/eframe/src/web/web_runner.rs | 2 +- crates/egui-wgpu/src/renderer.rs | 2 +- crates/egui/src/containers/scroll_area.rs | 2 +- crates/egui/src/data/key.rs | 2 +- crates/egui/src/input_state/mod.rs | 6 ++-- crates/egui/src/layout.rs | 2 +- crates/egui/src/lib.rs | 2 +- crates/egui/src/memory/mod.rs | 2 +- crates/egui/src/style.rs | 2 +- crates/egui/src/widgets/slider.rs | 2 +- crates/egui_demo_lib/src/demo/sliders.rs | 3 +- .../src/easy_mark/easy_mark_parser.rs | 2 +- crates/emath/src/numeric.rs | 8 ++--- crates/emath/src/rect.rs | 9 +++-- crates/emath/src/smart_aim.rs | 4 ++- crates/epaint/src/mutex.rs | 2 +- crates/epaint/src/text/text_layout_types.rs | 4 +-- examples/confirm_exit/Cargo.toml | 2 +- examples/custom_3d_glow/Cargo.toml | 2 +- examples/custom_font/Cargo.toml | 2 +- examples/custom_font_style/Cargo.toml | 2 +- examples/custom_keypad/Cargo.toml | 2 +- examples/custom_style/Cargo.toml | 2 +- examples/custom_window_frame/Cargo.toml | 2 +- examples/file_dialog/Cargo.toml | 2 +- examples/hello_world/Cargo.toml | 2 +- examples/hello_world_par/Cargo.toml | 6 ++-- examples/hello_world_simple/Cargo.toml | 2 +- examples/images/Cargo.toml | 2 +- examples/keyboard_events/Cargo.toml | 2 +- examples/multiple_viewports/Cargo.toml | 2 +- examples/puffin_profiler/Cargo.toml | 2 +- examples/screenshot/Cargo.toml | 2 +- examples/serial_windows/Cargo.toml | 2 +- examples/user_attention/Cargo.toml | 2 +- rust-toolchain | 2 +- scripts/check.sh | 2 +- scripts/clippy_wasm/clippy.toml | 5 ++- tests/test_egui_extras_compilation/Cargo.toml | 7 ++-- tests/test_inline_glow_paint/Cargo.toml | 2 +- tests/test_size_pass/Cargo.toml | 2 +- tests/test_ui_stack/Cargo.toml | 2 +- tests/test_viewports/Cargo.toml | 2 +- 50 files changed, 94 insertions(+), 103 deletions(-) diff --git a/.github/workflows/deploy_web_demo.yml b/.github/workflows/deploy_web_demo.yml index 7be0b42f358..c5924a3b0f0 100644 --- a/.github/workflows/deploy_web_demo.yml +++ b/.github/workflows/deploy_web_demo.yml @@ -39,7 +39,7 @@ jobs: with: profile: minimal target: wasm32-unknown-unknown - toolchain: 1.77.0 + toolchain: 1.79.0 override: true - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 16df33e4720..e669fe4c543 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -18,7 +18,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.77.0 + toolchain: 1.79.0 - name: Install packages (Linux) if: runner.os == 'Linux' @@ -83,7 +83,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.77.0 + toolchain: 1.79.0 targets: wasm32-unknown-unknown - run: sudo apt-get update && sudo apt-get install libgtk-3-dev libatk1.0-dev @@ -155,7 +155,7 @@ jobs: - uses: actions/checkout@v4 - uses: EmbarkStudios/cargo-deny-action@v1 with: - rust-version: "1.77.0" + rust-version: "1.79.0" log-level: error command: check arguments: --target ${{ matrix.target }} @@ -170,7 +170,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.77.0 + toolchain: 1.79.0 targets: aarch64-linux-android - name: Set up cargo cache @@ -189,7 +189,7 @@ jobs: - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.77.0 + toolchain: 1.79.0 targets: aarch64-apple-ios - name: Set up cargo cache @@ -208,7 +208,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.77.0 + toolchain: 1.79.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 @@ -232,7 +232,7 @@ jobs: lfs: true - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.77.0 + toolchain: 1.79.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 diff --git a/Cargo.toml b/Cargo.toml index 9fffbc80191..ab53c6cfbb8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ [workspace.package] edition = "2021" license = "MIT OR Apache-2.0" -rust-version = "1.77" +rust-version = "1.79" version = "0.29.1" @@ -145,6 +145,7 @@ disallowed_types = "warn" # See clippy.toml doc_link_with_quotes = "warn" doc_markdown = "warn" empty_enum = "warn" +empty_enum_variants_with_brackets = "warn" enum_glob_use = "warn" equatable_if_let = "warn" exit = "warn" @@ -169,6 +170,8 @@ inefficient_to_string = "warn" infinite_loop = "warn" into_iter_without_iter = "warn" invalid_upcast_comparisons = "warn" +iter_filter_is_ok = "warn" +iter_filter_is_some = "warn" iter_not_returning_iterator = "warn" iter_on_empty_collections = "warn" iter_on_single_items = "warn" @@ -185,6 +188,7 @@ macro_use_imports = "warn" manual_assert = "warn" manual_clamp = "warn" manual_instant_elapsed = "warn" +manual_is_variant_and = "warn" manual_let_else = "warn" manual_ok_or = "warn" manual_string_new = "warn" @@ -202,6 +206,7 @@ mismatching_type_param_order = "warn" missing_enforced_import_renames = "warn" missing_errors_doc = "warn" missing_safety_doc = "warn" +mixed_attributes_style = "warn" mut_mut = "warn" mutex_integer = "warn" needless_borrow = "warn" @@ -211,21 +216,25 @@ needless_pass_by_ref_mut = "warn" needless_pass_by_value = "warn" negative_feature_names = "warn" nonstandard_macro_braces = "warn" +option_as_ref_cloned = "warn" option_option = "warn" path_buf_push_overwrite = "warn" print_stderr = "warn" ptr_as_ptr = "warn" ptr_cast_constness = "warn" +pub_underscore_fields = "warn" pub_without_shorthand = "warn" rc_mutex = "warn" readonly_write_lock = "warn" redundant_type_annotations = "warn" +ref_as_ptr = "warn" ref_option_ref = "warn" ref_patterns = "warn" rest_pat_in_fully_bound_structs = "warn" same_functions_in_if_condition = "warn" semicolon_if_nothing_returned = "warn" single_match_else = "warn" +str_split_at_newline = "warn" str_to_string = "warn" string_add = "warn" string_add_assign = "warn" @@ -261,12 +270,15 @@ zero_sized_map_values = "warn" # TODO(emilk): enable more of these lints: iter_over_hash_type = "allow" -let_underscore_untyped = "allow" missing_assert_message = "allow" should_panic_without_expect = "allow" too_many_lines = "allow" unwrap_used = "allow" # TODO(emilk): We really wanna warn on this one +# These are meh: +assigning_clones = "allow" # No please +let_underscore_must_use = "allow" +let_underscore_untyped = "allow" manual_range_contains = "allow" # this one is just worse imho self_named_module_files = "allow" # Disabled waiting on https://github.com/rust-lang/rust-clippy/issues/9602 significant_drop_tightening = "allow" # Too many false positives diff --git a/clippy.toml b/clippy.toml index 05e436ac419..cd4a25cab4f 100644 --- a/clippy.toml +++ b/clippy.toml @@ -3,7 +3,7 @@ # ----------------------------------------------------------------------------- # Section identical to scripts/clippy_wasm/clippy.toml: -msrv = "1.77" +msrv = "1.79" allow-unwrap-in-tests = true @@ -69,9 +69,12 @@ disallowed-types = [ # Allow-list of words for markdown in docstrings https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown doc-valid-idents = [ - # You must also update the same list in the root `clippy.toml`! + # You must also update the same list in `scripts/clippy_wasm/clippy.toml`! "AccessKit", "WebGL", + "WebGL1", + "WebGL2", "WebGPU", + "VirtualBox", "..", ] diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index f567507c4da..9f4f6dde8b1 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -439,7 +439,7 @@ pub struct WebOptions { /// Unused by webgl context as of writing. pub depth_buffer: u8, - /// Which version of WebGl context to select + /// Which version of WebGL context to select /// /// Default: [`WebGlContextOption::BestFirst`]. #[cfg(feature = "glow")] diff --git a/crates/eframe/src/native/event_loop_context.rs b/crates/eframe/src/native/event_loop_context.rs index 8f54681a869..f1262f85358 100644 --- a/crates/eframe/src/native/event_loop_context.rs +++ b/crates/eframe/src/native/event_loop_context.rs @@ -14,7 +14,7 @@ impl EventLoopGuard { cell.get().is_none(), "Attempted to set a new event loop while one is already set" ); - cell.set(Some(event_loop as *const ActiveEventLoop)); + cell.set(Some(std::ptr::from_ref::(event_loop))); }); Self } diff --git a/crates/eframe/src/web/web_painter_wgpu.rs b/crates/eframe/src/web/web_painter_wgpu.rs index 2d5f5a4c3f0..ec487622e38 100644 --- a/crates/eframe/src/web/web_painter_wgpu.rs +++ b/crates/eframe/src/web/web_painter_wgpu.rs @@ -1,41 +1,13 @@ -use raw_window_handle::{ - DisplayHandle, HandleError, HasDisplayHandle, HasWindowHandle, RawDisplayHandle, - RawWindowHandle, WebDisplayHandle, WebWindowHandle, WindowHandle, -}; use std::sync::Arc; + use wasm_bindgen::JsValue; use web_sys::HtmlCanvasElement; -use crate::WebOptions; use egui_wgpu::{RenderState, SurfaceErrorAction, WgpuSetup}; -use super::web_painter::WebPainter; - -struct EguiWebWindow(u32); - -#[allow(unsafe_code)] -impl HasWindowHandle for EguiWebWindow { - fn window_handle(&self) -> Result, HandleError> { - // SAFETY: there is no lifetime here. - unsafe { - Ok(WindowHandle::borrow_raw(RawWindowHandle::Web( - WebWindowHandle::new(self.0), - ))) - } - } -} +use crate::WebOptions; -#[allow(unsafe_code)] -impl HasDisplayHandle for EguiWebWindow { - fn display_handle(&self) -> Result, HandleError> { - // SAFETY: there is no lifetime here. - unsafe { - Ok(DisplayHandle::borrow_raw(RawDisplayHandle::Web( - WebDisplayHandle::new(), - ))) - } - } -} +use super::web_painter::WebPainter; pub(crate) struct WebPainterWgpu { canvas: HtmlCanvasElement, diff --git a/crates/eframe/src/web/web_runner.rs b/crates/eframe/src/web/web_runner.rs index 46efa09bb33..6cbc371f34c 100644 --- a/crates/eframe/src/web/web_runner.rs +++ b/crates/eframe/src/web/web_runner.rs @@ -16,7 +16,7 @@ pub struct WebRunner { /// Have we ever panicked? panic_handler: PanicHandler, - /// If we ever panic during running, this RefCell is poisoned. + /// If we ever panic during running, this `RefCell` is poisoned. /// So before we use it, we need to check [`Self::panic_handler`]. runner: Rc>>, diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index a16b93781f6..b6d49d22d4f 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -125,7 +125,7 @@ pub struct ScreenDescriptor { /// Size of the window in physical pixels. pub size_in_pixels: [u32; 2], - /// HiDPI scale factor (pixels per point). + /// High-DPI scale factor (pixels per point). pub pixels_per_point: f32, } diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 3b3925c9dcd..8b8bd8ad524 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -39,7 +39,7 @@ pub struct State { scroll_start_offset_from_top_left: [Option; 2], /// Is the scroll sticky. This is true while scroll handle is in the end position - /// and remains that way until the user moves the scroll_handle. Once unstuck (false) + /// and remains that way until the user moves the `scroll_handle`. Once unstuck (false) /// it remains false until the scroll touches the end position, which reenables stickiness. scroll_stuck_to_end: Vec2b, diff --git a/crates/egui/src/data/key.rs b/crates/egui/src/data/key.rs index c43d1c5685d..a075b025a2f 100644 --- a/crates/egui/src/data/key.rs +++ b/crates/egui/src/data/key.rs @@ -55,7 +55,7 @@ pub enum Key { // `]` CloseBracket, - /// \`, also known as "backquote" or "grave" + /// Also known as "backquote" or "grave" Backtick, /// `-` diff --git a/crates/egui/src/input_state/mod.rs b/crates/egui/src/input_state/mod.rs index 7f743ee709a..99c166cf443 100644 --- a/crates/egui/src/input_state/mod.rs +++ b/crates/egui/src/input_state/mod.rs @@ -889,9 +889,9 @@ impl Default for PointerState { press_start_time: None, has_moved_too_much_for_a_click: false, started_decidedly_dragging: false, - last_click_time: std::f64::NEG_INFINITY, - last_last_click_time: std::f64::NEG_INFINITY, - last_move_time: std::f64::NEG_INFINITY, + last_click_time: f64::NEG_INFINITY, + last_last_click_time: f64::NEG_INFINITY, + last_move_time: f64::NEG_INFINITY, pointer_events: vec![], input_options: Default::default(), } diff --git a/crates/egui/src/layout.rs b/crates/egui/src/layout.rs index 32fd0d03ac4..0c9bb494133 100644 --- a/crates/egui/src/layout.rs +++ b/crates/egui/src/layout.rs @@ -2,7 +2,7 @@ use crate::{ emath::{pos2, vec2, Align2, NumExt, Pos2, Rect, Vec2}, Align, }; -use std::f32::INFINITY; +const INFINITY: f32 = f32::INFINITY; // ---------------------------------------------------------------------------- diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 79784c984a7..866d20fc64d 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -3,7 +3,7 @@ //! Try the live web demo: . Read more about egui at . //! //! `egui` is in heavy development, with each new version having breaking changes. -//! You need to have rust 1.77.0 or later to use `egui`. +//! You need to have rust 1.79.0 or later to use `egui`. //! //! To quickly get started with egui, you can take a look at [`eframe_template`](https://github.com/emilk/eframe_template) //! which uses [`eframe`](https://docs.rs/eframe). diff --git a/crates/egui/src/memory/mod.rs b/crates/egui/src/memory/mod.rs index e5ca8d42789..f49f1342e50 100644 --- a/crates/egui/src/memory/mod.rs +++ b/crates/egui/src/memory/mod.rs @@ -736,7 +736,7 @@ impl Focus { let current_rect = self.focus_widgets_cache.get(¤t_focused.id)?; - let mut best_score = std::f32::INFINITY; + let mut best_score = f32::INFINITY; let mut best_id = None; for (candidate_id, candidate_rect) in &self.focus_widgets_cache { diff --git a/crates/egui/src/style.rs b/crates/egui/src/style.rs index 0f6c9633ebd..14b5aecd6ee 100644 --- a/crates/egui/src/style.rs +++ b/crates/egui/src/style.rs @@ -288,7 +288,7 @@ pub struct Style { /// If true and scrolling is enabled for only one direction, allow horizontal scrolling without pressing shift pub always_scroll_the_only_direction: bool, - /// The animation that should be used when scrolling a [`crate::ScrollArea`] using e.g. [Ui::scroll_to_rect]. + /// The animation that should be used when scrolling a [`crate::ScrollArea`] using e.g. [`Ui::scroll_to_rect`]. pub scroll_animation: ScrollAnimation, } diff --git a/crates/egui/src/widgets/slider.rs b/crates/egui/src/widgets/slider.rs index 3c2bb973f88..7dc3a5cd568 100644 --- a/crates/egui/src/widgets/slider.rs +++ b/crates/egui/src/widgets/slider.rs @@ -1030,7 +1030,7 @@ impl<'a> Widget for Slider<'a> { // Logarithmic sliders are allowed to include zero and infinity, // even though mathematically it doesn't make sense. -use std::f64::INFINITY; +const INFINITY: f64 = f64::INFINITY; /// When the user asks for an infinitely large range (e.g. logarithmic from zero), /// give a scale that this many orders of magnitude in size. diff --git a/crates/egui_demo_lib/src/demo/sliders.rs b/crates/egui_demo_lib/src/demo/sliders.rs index d15d9aa3100..ef8bdb0cd11 100644 --- a/crates/egui_demo_lib/src/demo/sliders.rs +++ b/crates/egui_demo_lib/src/demo/sliders.rs @@ -1,5 +1,4 @@ use egui::{style::HandleShape, Slider, SliderClamping, SliderOrientation, Ui}; -use std::f64::INFINITY; /// Showcase sliders #[derive(PartialEq)] @@ -77,7 +76,7 @@ impl crate::View for Sliders { let (type_min, type_max) = if *integer { ((i32::MIN as f64), (i32::MAX as f64)) } else if *logarithmic { - (-INFINITY, INFINITY) + (-f64::INFINITY, f64::INFINITY) } else { (-1e5, 1e5) // linear sliders make little sense with huge numbers }; diff --git a/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs b/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs index 75d8135dff5..ed3ebe7f90f 100644 --- a/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs +++ b/crates/egui_demo_lib/src/easy_mark/easy_mark_parser.rs @@ -13,7 +13,7 @@ pub enum Item<'a> { // TODO(emilk): add Style here so empty heading still uses up the right amount of space. Newline, - /// + /// Text Text(Style, &'a str), /// title, url diff --git a/crates/emath/src/numeric.rs b/crates/emath/src/numeric.rs index 03d00077129..9a7814b23d2 100644 --- a/crates/emath/src/numeric.rs +++ b/crates/emath/src/numeric.rs @@ -18,8 +18,8 @@ macro_rules! impl_numeric_float { ($t: ident) => { impl Numeric for $t { const INTEGRAL: bool = false; - const MIN: Self = std::$t::MIN; - const MAX: Self = std::$t::MAX; + const MIN: Self = $t::MIN; + const MAX: Self = $t::MAX; #[inline(always)] fn to_f64(self) -> f64 { @@ -44,8 +44,8 @@ macro_rules! impl_numeric_integer { ($t: ident) => { impl Numeric for $t { const INTEGRAL: bool = true; - const MIN: Self = std::$t::MIN; - const MAX: Self = std::$t::MAX; + const MIN: Self = $t::MIN; + const MAX: Self = $t::MAX; #[inline(always)] fn to_f64(self) -> f64 { diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index 6c0677ad55e..12770fa3e1c 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -1,4 +1,3 @@ -use std::f32::INFINITY; use std::fmt; use crate::{lerp, pos2, vec2, Div, Mul, Pos2, Rangef, Rot2, Vec2}; @@ -33,8 +32,8 @@ pub struct Rect { impl Rect { /// Infinite rectangle that contains every point. pub const EVERYTHING: Self = Self { - min: pos2(-INFINITY, -INFINITY), - max: pos2(INFINITY, INFINITY), + min: pos2(-f32::INFINITY, -f32::INFINITY), + max: pos2(f32::INFINITY, f32::INFINITY), }; /// The inverse of [`Self::EVERYTHING`]: stretches from positive infinity to negative infinity. @@ -53,8 +52,8 @@ impl Rect { /// assert_eq!(rect, Rect::from_min_max(pos2(0.0, 1.0), pos2(2.0, 3.0))) /// ``` pub const NOTHING: Self = Self { - min: pos2(INFINITY, INFINITY), - max: pos2(-INFINITY, -INFINITY), + min: pos2(f32::INFINITY, f32::INFINITY), + max: pos2(-f32::INFINITY, -f32::INFINITY), }; /// An invalid [`Rect`] filled with [`f32::NAN`]. diff --git a/crates/emath/src/smart_aim.rs b/crates/emath/src/smart_aim.rs index 88b807cf80b..72094706995 100644 --- a/crates/emath/src/smart_aim.rs +++ b/crates/emath/src/smart_aim.rs @@ -138,7 +138,9 @@ fn test_aim() { assert_eq!(best_in_range_f64(99.999, 100.000), 100.0); assert_eq!(best_in_range_f64(10.001, 100.001), 100.0); - use std::f64::{INFINITY, NAN, NEG_INFINITY}; + const NAN: f64 = f64::NAN; + const INFINITY: f64 = f64::INFINITY; + const NEG_INFINITY: f64 = f64::NEG_INFINITY; assert!(best_in_range_f64(NAN, NAN).is_nan()); assert_eq!(best_in_range_f64(NAN, 1.2), 1.2); assert_eq!(best_in_range_f64(NAN, INFINITY), INFINITY); diff --git a/crates/epaint/src/mutex.rs b/crates/epaint/src/mutex.rs index 157701c2be0..bd984a1d0ec 100644 --- a/crates/epaint/src/mutex.rs +++ b/crates/epaint/src/mutex.rs @@ -75,7 +75,7 @@ mod mutex_impl { // Detect if we are recursively taking out a lock on this mutex. // use a pointer to the inner data as an id for this lock - let ptr = (&self.0 as *const parking_lot::Mutex<_>).cast::<()>(); + let ptr = std::ptr::from_ref::>(&self.0).cast::<()>(); // Store it in thread local storage while we have a lock guard taken out HELD_LOCKS_TLS.with(|held_locks| { diff --git a/crates/epaint/src/text/text_layout_types.rs b/crates/epaint/src/text/text_layout_types.rs index 17826e6afb1..64dd827148c 100644 --- a/crates/epaint/src/text/text_layout_types.rs +++ b/crates/epaint/src/text/text_layout_types.rs @@ -623,10 +623,10 @@ pub struct Glyph { /// The row/line height of this font. pub font_height: f32, - /// The ascent of the sub-font within the font ("FontImpl"). + /// The ascent of the sub-font within the font (`FontImpl`). pub font_impl_ascent: f32, - /// The row/line height of the sub-font within the font ("FontImpl"). + /// The row/line height of the sub-font within the font (`FontImpl`). pub font_impl_height: f32, /// Position and size of the glyph in the font texture, in texels. diff --git a/examples/confirm_exit/Cargo.toml b/examples/confirm_exit/Cargo.toml index 4f31cc6d7cc..d4a21060b76 100644 --- a/examples/confirm_exit/Cargo.toml +++ b/examples/confirm_exit/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/custom_3d_glow/Cargo.toml b/examples/custom_3d_glow/Cargo.toml index 7bbea3b07de..d1dcc056949 100644 --- a/examples/custom_3d_glow/Cargo.toml +++ b/examples/custom_3d_glow/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/custom_font/Cargo.toml b/examples/custom_font/Cargo.toml index d7cecf07312..ee769cc62d5 100644 --- a/examples/custom_font/Cargo.toml +++ b/examples/custom_font/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/custom_font_style/Cargo.toml b/examples/custom_font_style/Cargo.toml index 54d037d15ed..f25676e87a1 100644 --- a/examples/custom_font_style/Cargo.toml +++ b/examples/custom_font_style/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["tami5 "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/custom_keypad/Cargo.toml b/examples/custom_keypad/Cargo.toml index 088e7a3fc41..dc3c62dddb8 100644 --- a/examples/custom_keypad/Cargo.toml +++ b/examples/custom_keypad/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Varphone Wong "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/custom_style/Cargo.toml b/examples/custom_style/Cargo.toml index cd4f4063bee..6299e1aee35 100644 --- a/examples/custom_style/Cargo.toml +++ b/examples/custom_style/Cargo.toml @@ -3,7 +3,7 @@ name = "custom_style" version = "0.1.0" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/custom_window_frame/Cargo.toml b/examples/custom_window_frame/Cargo.toml index 5e4941e3df8..848189084ad 100644 --- a/examples/custom_window_frame/Cargo.toml +++ b/examples/custom_window_frame/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/file_dialog/Cargo.toml b/examples/file_dialog/Cargo.toml index 7adb8e1b8a9..dc58e0ba2e7 100644 --- a/examples/file_dialog/Cargo.toml +++ b/examples/file_dialog/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/hello_world/Cargo.toml b/examples/hello_world/Cargo.toml index e5acf4d89bb..6e7dd8d00f1 100644 --- a/examples/hello_world/Cargo.toml +++ b/examples/hello_world/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/hello_world_par/Cargo.toml b/examples/hello_world_par/Cargo.toml index 07cdd4858c1..d486e35791d 100644 --- a/examples/hello_world_par/Cargo.toml +++ b/examples/hello_world_par/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Maxim Osipenko "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] @@ -29,6 +29,4 @@ env_logger = { version = "0.10", default-features = false, features = [ ] } # This is normally enabled by eframe/default, which is not being used here # because of accesskit, as mentioned above -winit = { workspace = true, features = [ - "default" -] } +winit = { workspace = true, features = ["default"] } diff --git a/examples/hello_world_simple/Cargo.toml b/examples/hello_world_simple/Cargo.toml index 169504409f7..0d77c65e316 100644 --- a/examples/hello_world_simple/Cargo.toml +++ b/examples/hello_world_simple/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/images/Cargo.toml b/examples/images/Cargo.toml index c564f4c1590..4759e2d128e 100644 --- a/examples/images/Cargo.toml +++ b/examples/images/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Jan Procházka "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/keyboard_events/Cargo.toml b/examples/keyboard_events/Cargo.toml index c2cd90ce575..e587764bacf 100644 --- a/examples/keyboard_events/Cargo.toml +++ b/examples/keyboard_events/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Jose Palazon "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/multiple_viewports/Cargo.toml b/examples/multiple_viewports/Cargo.toml index 089d0840142..9910aed5a2d 100644 --- a/examples/multiple_viewports/Cargo.toml +++ b/examples/multiple_viewports/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/puffin_profiler/Cargo.toml b/examples/puffin_profiler/Cargo.toml index 8f200bd628d..d2cbce19052 100644 --- a/examples/puffin_profiler/Cargo.toml +++ b/examples/puffin_profiler/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/screenshot/Cargo.toml b/examples/screenshot/Cargo.toml index cc4733594dd..2e74482a64e 100644 --- a/examples/screenshot/Cargo.toml +++ b/examples/screenshot/Cargo.toml @@ -7,7 +7,7 @@ authors = [ ] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/serial_windows/Cargo.toml b/examples/serial_windows/Cargo.toml index 069d7184481..c377524c69c 100644 --- a/examples/serial_windows/Cargo.toml +++ b/examples/serial_windows/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/examples/user_attention/Cargo.toml b/examples/user_attention/Cargo.toml index a9fd5aa0373..3fbc75e260e 100644 --- a/examples/user_attention/Cargo.toml +++ b/examples/user_attention/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["TicClick "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/rust-toolchain b/rust-toolchain index ed934c15761..9fdafb7a67a 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -5,6 +5,6 @@ # to the user in the error, instead of "error: invalid channel name '[toolchain]'". [toolchain] -channel = "1.77.0" +channel = "1.79.0" components = ["rustfmt", "clippy"] targets = ["wasm32-unknown-unknown"] diff --git a/scripts/check.sh b/scripts/check.sh index 5f316d57a69..3129f2ac114 100755 --- a/scripts/check.sh +++ b/scripts/check.sh @@ -9,7 +9,7 @@ set -x # Checks all tests, lints etc. # Basically does what the CI does. -cargo +1.77.0 install --quiet typos-cli +cargo +1.79.0 install --quiet typos-cli export RUSTFLAGS="-D warnings" export RUSTDOCFLAGS="-D warnings" # https://github.com/emilk/egui/pull/1454 diff --git a/scripts/clippy_wasm/clippy.toml b/scripts/clippy_wasm/clippy.toml index f0e91004a81..0f7fc92dcc7 100644 --- a/scripts/clippy_wasm/clippy.toml +++ b/scripts/clippy_wasm/clippy.toml @@ -6,7 +6,7 @@ # ----------------------------------------------------------------------------- # Section identical to the root clippy.toml: -msrv = "1.77" +msrv = "1.79" allow-unwrap-in-tests = true @@ -47,6 +47,9 @@ doc-valid-idents = [ # You must also update the same list in the root `clippy.toml`! "AccessKit", "WebGL", + "WebGL1", + "WebGL2", "WebGPU", + "VirtualBox", "..", ] diff --git a/tests/test_egui_extras_compilation/Cargo.toml b/tests/test_egui_extras_compilation/Cargo.toml index bccea8a3216..1a310566f2a 100644 --- a/tests/test_egui_extras_compilation/Cargo.toml +++ b/tests/test_egui_extras_compilation/Cargo.toml @@ -3,14 +3,17 @@ name = "test_egui_extras_compilation" version = "0.1.0" license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] workspace = true [package.metadata.cargo-machete] -ignored = ["eframe", "egui_extras"] # We don't use them, just check that things compile +ignored = [ + "eframe", + "egui_extras", +] # We don't use them, just check that things compile [dependencies] eframe = { workspace = true, features = ["default", "persistence"] } diff --git a/tests/test_inline_glow_paint/Cargo.toml b/tests/test_inline_glow_paint/Cargo.toml index 975f777d72b..5ded3cc356b 100644 --- a/tests/test_inline_glow_paint/Cargo.toml +++ b/tests/test_inline_glow_paint/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/tests/test_size_pass/Cargo.toml b/tests/test_size_pass/Cargo.toml index 21d645cef5e..d6ee661e6b6 100644 --- a/tests/test_size_pass/Cargo.toml +++ b/tests/test_size_pass/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Emil Ernerfeldt "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/tests/test_ui_stack/Cargo.toml b/tests/test_ui_stack/Cargo.toml index e87845bc043..df2e2bf15c2 100644 --- a/tests/test_ui_stack/Cargo.toml +++ b/tests/test_ui_stack/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["Antoine Beyeler "] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] diff --git a/tests/test_viewports/Cargo.toml b/tests/test_viewports/Cargo.toml index 6edac0bd5e1..cb962411558 100644 --- a/tests/test_viewports/Cargo.toml +++ b/tests/test_viewports/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" authors = ["konkitoman"] license = "MIT OR Apache-2.0" edition = "2021" -rust-version = "1.77" +rust-version = "1.79" publish = false [lints] From 6833cf56e17d2581163c43eb36dc3ee5d758771b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jochen=20G=C3=B6rtler?= Date: Mon, 2 Dec 2024 09:20:59 +0100 Subject: [PATCH 4/8] Add new `Rect::intersects_ray_from_center` method (#5415) Title. * [x] I have followed the instructions in the PR template --- crates/emath/src/rect.rs | 81 +++++++++++++++++++++++++++++++++++++++ crates/emath/src/vec2.rs | 82 ++++++++++++++++++++++++++++------------ 2 files changed, 139 insertions(+), 24 deletions(-) diff --git a/crates/emath/src/rect.rs b/crates/emath/src/rect.rs index 12770fa3e1c..8b655bd722f 100644 --- a/crates/emath/src/rect.rs +++ b/crates/emath/src/rect.rs @@ -649,6 +649,8 @@ impl Rect { /// /// A ray that starts inside the rect will return `true`. pub fn intersects_ray(&self, o: Pos2, d: Vec2) -> bool { + debug_assert!(d.is_normalized(), "expected normalized direction"); + let mut tmin = -f32::INFINITY; let mut tmax = f32::INFINITY; @@ -670,6 +672,32 @@ impl Rect { 0.0 <= tmax && tmin <= tmax } + + /// Where does a ray from the center intersect the rectangle? + /// + /// `d` is the direction of the ray and assumed to be normalized. + pub fn intersects_ray_from_center(&self, d: Vec2) -> Pos2 { + debug_assert!(d.is_normalized(), "expected normalized direction"); + + let mut tmin = f32::NEG_INFINITY; + let mut tmax = f32::INFINITY; + + for i in 0..2 { + let inv_d = 1.0 / -d[i]; + let mut t0 = (self.min[i] - self.center()[i]) * inv_d; + let mut t1 = (self.max[i] - self.center()[i]) * inv_d; + + if inv_d < 0.0 { + std::mem::swap(&mut t0, &mut t1); + } + + tmin = tmin.max(t0); + tmax = tmax.min(t1); + } + + let t = tmax.min(tmin); + self.center() + t * -d + } } impl fmt::Debug for Rect { @@ -792,4 +820,57 @@ mod tests { println!("Leftward ray from right:"); assert!(rect.intersects_ray(pos2(4.0, 2.0), Vec2::LEFT)); } + + #[test] + fn test_ray_from_center_intersection() { + let rect = Rect::from_min_max(pos2(1.0, 1.0), pos2(3.0, 3.0)); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::RIGHT), + pos2(3.0, 2.0), + "rightward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::UP), + pos2(2.0, 1.0), + "upward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::LEFT), + pos2(1.0, 2.0), + "leftward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center(Vec2::DOWN), + pos2(2.0, 3.0), + "downward ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::LEFT + Vec2::DOWN).normalized()), + pos2(1.0, 3.0), + "bottom-left corner ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::LEFT + Vec2::UP).normalized()), + pos2(1.0, 1.0), + "top-left corner ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::DOWN).normalized()), + pos2(3.0, 3.0), + "bottom-right corner ray" + ); + + assert_eq!( + rect.intersects_ray_from_center((Vec2::RIGHT + Vec2::UP).normalized()), + pos2(3.0, 1.0), + "top-right corner ray" + ); + } } diff --git a/crates/emath/src/vec2.rs b/crates/emath/src/vec2.rs index 03e0715c20a..9a173348b03 100644 --- a/crates/emath/src/vec2.rs +++ b/crates/emath/src/vec2.rs @@ -176,6 +176,12 @@ impl Vec2 { } } + /// Checks if `self` has length `1.0` up to a precision of `1e-6`. + #[inline(always)] + pub fn is_normalized(self) -> bool { + (self.length_sq() - 1.0).abs() < 2e-6 + } + /// Rotates the vector by 90°, i.e positive X to positive Y /// (clockwise in egui coordinates). #[inline(always)] @@ -497,8 +503,10 @@ impl fmt::Display for Vec2 { } } -#[test] -fn test_vec2() { +#[cfg(test)] +mod test { + use super::*; + macro_rules! almost_eq { ($left: expr, $right: expr) => { let left = $left; @@ -506,32 +514,58 @@ fn test_vec2() { assert!((left - right).abs() < 1e-6, "{} != {}", left, right); }; } - use std::f32::consts::TAU; - assert_eq!(Vec2::ZERO.angle(), 0.0); - assert_eq!(Vec2::angled(0.0).angle(), 0.0); - assert_eq!(Vec2::angled(1.0).angle(), 1.0); - assert_eq!(Vec2::X.angle(), 0.0); - assert_eq!(Vec2::Y.angle(), 0.25 * TAU); + #[test] + fn test_vec2() { + use std::f32::consts::TAU; + + assert_eq!(Vec2::ZERO.angle(), 0.0); + assert_eq!(Vec2::angled(0.0).angle(), 0.0); + assert_eq!(Vec2::angled(1.0).angle(), 1.0); + assert_eq!(Vec2::X.angle(), 0.0); + assert_eq!(Vec2::Y.angle(), 0.25 * TAU); + + assert_eq!(Vec2::RIGHT.angle(), 0.0); + assert_eq!(Vec2::DOWN.angle(), 0.25 * TAU); + almost_eq!(Vec2::LEFT.angle(), 0.50 * TAU); + assert_eq!(Vec2::UP.angle(), -0.25 * TAU); - assert_eq!(Vec2::RIGHT.angle(), 0.0); - assert_eq!(Vec2::DOWN.angle(), 0.25 * TAU); - almost_eq!(Vec2::LEFT.angle(), 0.50 * TAU); - assert_eq!(Vec2::UP.angle(), -0.25 * TAU); + let mut assignment = vec2(1.0, 2.0); + assignment += vec2(3.0, 4.0); + assert_eq!(assignment, vec2(4.0, 6.0)); - let mut assignment = vec2(1.0, 2.0); - assignment += vec2(3.0, 4.0); - assert_eq!(assignment, vec2(4.0, 6.0)); + let mut assignment = vec2(4.0, 6.0); + assignment -= vec2(1.0, 2.0); + assert_eq!(assignment, vec2(3.0, 4.0)); - let mut assignment = vec2(4.0, 6.0); - assignment -= vec2(1.0, 2.0); - assert_eq!(assignment, vec2(3.0, 4.0)); + let mut assignment = vec2(1.0, 2.0); + assignment *= 2.0; + assert_eq!(assignment, vec2(2.0, 4.0)); - let mut assignment = vec2(1.0, 2.0); - assignment *= 2.0; - assert_eq!(assignment, vec2(2.0, 4.0)); + let mut assignment = vec2(2.0, 4.0); + assignment /= 2.0; + assert_eq!(assignment, vec2(1.0, 2.0)); + } + + #[test] + fn test_vec2_normalized() { + fn generate_spiral(n: usize, start: Vec2, end: Vec2) -> impl Iterator { + let angle_step = 2.0 * std::f32::consts::PI / n as f32; + let radius_step = (end.length() - start.length()) / n as f32; + + (0..n).map(move |i| { + let angle = i as f32 * angle_step; + let radius = start.length() + i as f32 * radius_step; + let x = radius * angle.cos(); + let y = radius * angle.sin(); + vec2(x, y) + }) + } - let mut assignment = vec2(2.0, 4.0); - assignment /= 2.0; - assert_eq!(assignment, vec2(1.0, 2.0)); + for v in generate_spiral(40, Vec2::splat(0.1), Vec2::splat(2.0)) { + let vn = v.normalized(); + almost_eq!(vn.length(), 1.0); + assert!(vn.is_normalized()); + } + } } From 4f7f23ef5eddd839654799d45ba377664131a7d1 Mon Sep 17 00:00:00 2001 From: Juan Campa Date: Mon, 2 Dec 2024 03:29:06 -0500 Subject: [PATCH 5/8] Fix cursor clipping in `TextEdit` inside a `ScrollArea` (#3660) * Closes #1531 ### Before Notice how the cursor hides after third enter and when the line is long. https://github.com/user-attachments/assets/8e45736e-d6c7-4dc6-94d0-213188c199ff ### After Cursor is always visible https://github.com/user-attachments/assets/43200683-3524-471b-990a-eb7b49385fa9 - `ScrollArea` now checks if there's a `scroll_target` in `begin`, if there is, it saves it because it's not from its children, then restore it in `end`. - `TextEdit` now allocates additional space if its galley grows during the frame. This is needed so that any surrounding `ScrollArea` can bring the cursor to view, otherwise the cursor lays outside the the `ScrollArea`'s `content_ui`. --- crates/egui/src/containers/scroll_area.rs | 22 +++++++++++++++++++- crates/egui/src/widgets/text_edit/builder.rs | 16 +++++++++++--- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 8b8bd8ad524..6065092cd51 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -499,6 +499,11 @@ struct Prepared { scrolling_enabled: bool, stick_to_end: Vec2b, + + /// If there was a scroll target before the ScrollArea was added this frame, it's + /// not for us to handle so we save it and restore it after this ScrollArea is done. + saved_scroll_target: [Option; 2], + animated: bool, } @@ -693,6 +698,10 @@ impl ScrollArea { } } + let saved_scroll_target = content_ui + .ctx() + .pass_state_mut(|state| std::mem::take(&mut state.scroll_target)); + Prepared { id, state, @@ -707,6 +716,7 @@ impl ScrollArea { viewport, scrolling_enabled, stick_to_end, + saved_scroll_target, animated, } } @@ -820,6 +830,7 @@ impl Prepared { viewport: _, scrolling_enabled, stick_to_end, + saved_scroll_target, animated, } = self; @@ -853,7 +864,7 @@ impl Prepared { let (start, end) = (range.min, range.max); let clip_start = clip_rect.min[d]; let clip_end = clip_rect.max[d]; - let mut spacing = ui.spacing().item_spacing[d]; + let mut spacing = content_ui.spacing().item_spacing[d]; let delta_update = if let Some(align) = align { let center_factor = align.to_factor(); @@ -902,6 +913,15 @@ impl Prepared { } } + // Restore scroll target meant for ScrollAreas up the stack (if any) + ui.ctx().pass_state_mut(|state| { + for d in 0..2 { + if saved_scroll_target[d].is_some() { + state.scroll_target[d] = saved_scroll_target[d].clone(); + }; + } + }); + let inner_rect = { // At this point this is the available size for the inner rect. let mut inner_size = inner_rect.size(); diff --git a/crates/egui/src/widgets/text_edit/builder.rs b/crates/egui/src/widgets/text_edit/builder.rs index 5ce5749b97d..7c37c6a60cf 100644 --- a/crates/egui/src/widgets/text_edit/builder.rs +++ b/crates/egui/src/widgets/text_edit/builder.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use emath::Rect; use epaint::text::{cursor::CCursor, Galley, LayoutJob}; use crate::{ @@ -720,6 +721,16 @@ impl<'t> TextEdit<'t> { } } + // Allocate additional space if edits were made this frame that changed the size. This is important so that, + // if there's a ScrollArea, it can properly scroll to the cursor. + let extra_size = galley.size() - rect.size(); + if extra_size.x > 0.0 || extra_size.y > 0.0 { + ui.allocate_rect( + Rect::from_min_size(outer_rect.max, extra_size), + Sense::hover(), + ); + } + painter.galley(galley_pos, galley.clone(), text_color); if has_focus { @@ -727,10 +738,9 @@ impl<'t> TextEdit<'t> { let primary_cursor_rect = cursor_rect(galley_pos, &galley, &cursor_range.primary, row_height); - let is_fully_visible = ui.clip_rect().contains_rect(rect); // TODO(emilk): remove this HACK workaround for https://github.com/emilk/egui/issues/1531 - if (response.changed || selection_changed) && !is_fully_visible { + if response.changed || selection_changed { // Scroll to keep primary cursor in view: - ui.scroll_to_rect(primary_cursor_rect, None); + ui.scroll_to_rect(primary_cursor_rect + margin, None); } if text.is_mutable() && interactive { From 6a1131f1c90409d7bc766d001b892b22a5e3322b Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 3 Dec 2024 09:55:25 +0100 Subject: [PATCH 6/8] Fix docstring backticks --- crates/egui/src/containers/scroll_area.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 6065092cd51..3c14a02e5e1 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -500,8 +500,8 @@ struct Prepared { scrolling_enabled: bool, stick_to_end: Vec2b, - /// If there was a scroll target before the ScrollArea was added this frame, it's - /// not for us to handle so we save it and restore it after this ScrollArea is done. + /// If there was a scroll target before the [`ScrollArea`] was added this frame, it's + /// not for us to handle so we save it and restore it after this [`ScrollArea`] is done. saved_scroll_target: [Option; 2], animated: bool, From a9c76ba7a60d96293cf1f7ed57825a6b1c7e983e Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 3 Dec 2024 10:08:55 +0100 Subject: [PATCH 7/8] Allow attaching custom user data to a screenshot command (#5416) This lets users trigger a screenshot from anywhere, and then when they get back the results they have some context about what part of their code triggered the screenshot. --- crates/eframe/src/native/glow_integration.rs | 3 +- crates/eframe/src/native/wgpu_integration.rs | 45 ++++++++---- crates/egui-winit/src/lib.rs | 6 +- crates/egui/src/data/input.rs | 4 ++ crates/egui/src/data/mod.rs | 2 + crates/egui/src/data/user_data.rs | 74 ++++++++++++++++++++ crates/egui/src/lib.rs | 2 +- crates/egui/src/viewport.rs | 6 +- examples/screenshot/src/main.rs | 10 ++- 9 files changed, 129 insertions(+), 23 deletions(-) create mode 100644 crates/egui/src/data/user_data.rs diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 2bd80cecbc3..51c0cadce2a 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -661,13 +661,14 @@ impl<'app> GlowWinitRunning<'app> { { for action in viewport.actions_requested.drain() { match action { - ActionRequested::Screenshot => { + ActionRequested::Screenshot(user_data) => { let screenshot = painter.read_screen_rgba(screen_size_in_pixels); egui_winit .egui_input_mut() .events .push(egui::Event::Screenshot { viewport_id, + user_data, image: screenshot.into(), }); } diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index 997383f85c3..d13bed0bf41 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -643,10 +643,16 @@ impl<'app> WgpuWinitRunning<'app> { let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point); - let screenshot_requested = viewport - .actions_requested - .take(&ActionRequested::Screenshot) - .is_some(); + let mut screenshot_commands = vec![]; + viewport.actions_requested.retain(|cmd| { + if let ActionRequested::Screenshot(info) = cmd { + screenshot_commands.push(info.clone()); + false + } else { + true + } + }); + let screenshot_requested = !screenshot_commands.is_empty(); let (vsync_secs, screenshot) = painter.paint_and_update_textures( viewport_id, pixels_per_point, @@ -655,19 +661,32 @@ impl<'app> WgpuWinitRunning<'app> { &textures_delta, screenshot_requested, ); - if let Some(screenshot) = screenshot { - egui_winit - .egui_input_mut() - .events - .push(egui::Event::Screenshot { - viewport_id, - image: screenshot.into(), - }); + match (screenshot_requested, screenshot) { + (false, None) => {} + (true, Some(screenshot)) => { + let screenshot = Arc::new(screenshot); + for user_data in screenshot_commands { + egui_winit + .egui_input_mut() + .events + .push(egui::Event::Screenshot { + viewport_id, + user_data, + image: screenshot.clone(), + }); + } + } + (true, None) => { + log::error!("Bug in egui_wgpu: screenshot requested, but no screenshot was taken"); + } + (false, Some(_)) => { + log::warn!("Bug in egui_wgpu: Got screenshot without requesting it"); + } } for action in viewport.actions_requested.drain() { match action { - ActionRequested::Screenshot => { + ActionRequested::Screenshot { .. } => { // already handled above } ActionRequested::Cut => { diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index a78339d01fe..1cb2d502c5d 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -1301,7 +1301,7 @@ fn translate_cursor(cursor_icon: egui::CursorIcon) -> Option { - actions_requested.insert(ActionRequested::Screenshot); + ViewportCommand::Screenshot(user_data) => { + actions_requested.insert(ActionRequested::Screenshot(user_data)); } ViewportCommand::RequestCut => { actions_requested.insert(ActionRequested::Cut); diff --git a/crates/egui/src/data/input.rs b/crates/egui/src/data/input.rs index a1b9783280e..7987ea61225 100644 --- a/crates/egui/src/data/input.rs +++ b/crates/egui/src/data/input.rs @@ -529,6 +529,10 @@ pub enum Event { /// The reply of a screenshot requested with [`crate::ViewportCommand::Screenshot`]. Screenshot { viewport_id: crate::ViewportId, + + /// Whatever was passed to [`crate::ViewportCommand::Screenshot`]. + user_data: crate::UserData, + image: std::sync::Arc, }, } diff --git a/crates/egui/src/data/mod.rs b/crates/egui/src/data/mod.rs index bfe1e8a327d..f6f267dd06a 100644 --- a/crates/egui/src/data/mod.rs +++ b/crates/egui/src/data/mod.rs @@ -3,5 +3,7 @@ pub mod input; mod key; pub mod output; +mod user_data; pub use key::Key; +pub use user_data::UserData; diff --git a/crates/egui/src/data/user_data.rs b/crates/egui/src/data/user_data.rs new file mode 100644 index 00000000000..20bf5e1a123 --- /dev/null +++ b/crates/egui/src/data/user_data.rs @@ -0,0 +1,74 @@ +use std::{any::Any, sync::Arc}; + +/// A wrapper around `dyn Any`, used for passing custom user data +/// to [`crate::ViewportCommand::Screenshot`]. +#[derive(Clone, Debug, Default)] +pub struct UserData { + /// A user value given to the screenshot command, + /// that will be returned in [`crate::Event::Screenshot`]. + pub data: Option>, +} + +impl UserData { + /// You can also use [`Self::default`]. + pub fn new(user_info: impl Any + Send + Sync) -> Self { + Self { + data: Some(Arc::new(user_info)), + } + } +} + +impl PartialEq for UserData { + fn eq(&self, other: &Self) -> bool { + match (&self.data, &other.data) { + (Some(a), Some(b)) => Arc::ptr_eq(a, b), + (None, None) => true, + _ => false, + } + } +} + +impl Eq for UserData {} + +impl std::hash::Hash for UserData { + fn hash(&self, state: &mut H) { + self.data.as_ref().map(Arc::as_ptr).hash(state); + } +} + +#[cfg(feature = "serde")] +impl serde::Serialize for UserData { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serializer.serialize_none() // can't serialize an `Any` + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for UserData { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct UserDataVisitor; + + impl<'de> serde::de::Visitor<'de> for UserDataVisitor { + type Value = UserData; + + fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + formatter.write_str("a None value") + } + + fn visit_none(self) -> Result + where + E: serde::de::Error, + { + Ok(UserData::default()) + } + } + + deserializer.deserialize_option(UserDataVisitor) + } +} diff --git a/crates/egui/src/lib.rs b/crates/egui/src/lib.rs index 866d20fc64d..18b99c69a1a 100644 --- a/crates/egui/src/lib.rs +++ b/crates/egui/src/lib.rs @@ -471,7 +471,7 @@ pub use self::{ output::{ self, CursorIcon, FullOutput, OpenUrl, PlatformOutput, UserAttentionType, WidgetInfo, }, - Key, + Key, UserData, }, drag_and_drop::DragAndDrop, epaint::text::TextWrapMode, diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 31cbc623e39..d8b26429c77 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -1058,8 +1058,8 @@ pub enum ViewportCommand { /// Take a screenshot. /// - /// The results are returned in `crate::Event::Screenshot`. - Screenshot, + /// The results are returned in [`crate::Event::Screenshot`]. + Screenshot(crate::UserData), /// Request cut of the current selection /// @@ -1100,6 +1100,8 @@ impl ViewportCommand { } } +// ---------------------------------------------------------------------------- + /// Describes a viewport, i.e. a native window. /// /// This is returned by [`crate::Context::run`] on each frame, and should be applied diff --git a/examples/screenshot/src/main.rs b/examples/screenshot/src/main.rs index 88b57f20e7a..1dd0bbf5076 100644 --- a/examples/screenshot/src/main.rs +++ b/examples/screenshot/src/main.rs @@ -45,7 +45,7 @@ impl eframe::App for MyApp { if ui.button("save to 'top_left.png'").clicked() { self.save_to_file = true; - ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot); + ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot(Default::default())); } ui.with_layout(egui::Layout::top_down(egui::Align::RIGHT), |ui| { @@ -58,9 +58,13 @@ impl eframe::App for MyApp { } else { ctx.set_theme(egui::Theme::Light); }; - ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot); + ctx.send_viewport_cmd( + egui::ViewportCommand::Screenshot(Default::default()), + ); } else if ui.button("take screenshot!").clicked() { - ctx.send_viewport_cmd(egui::ViewportCommand::Screenshot); + ctx.send_viewport_cmd( + egui::ViewportCommand::Screenshot(Default::default()), + ); } }); }); From 8647b56b3142ae4a69fb56cc9ef838c583bcd923 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 3 Dec 2024 10:33:10 +0100 Subject: [PATCH 8/8] Update snapshot for `Code Example` --- crates/egui_demo_lib/tests/snapshots/demos/Code Example.png | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png index 093b2c6a33b..162fc51a1df 100644 --- a/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png +++ b/crates/egui_demo_lib/tests/snapshots/demos/Code Example.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b8d4f004ee11ea68ae0f30657601b6e51403fcc3ca91fa5b8cdcb58585d8d40d -size 78318 +oid sha256:01aaa4ef1a167a94fa1e5163550aabe4fa5e9f3a012b26170fe3088a6ca32d94 +size 81064