diff --git a/.github/workflows/cargo_machete.yml b/.github/workflows/cargo_machete.yml index 7e5747800..1dc162e56 100644 --- a/.github/workflows/cargo_machete.yml +++ b/.github/workflows/cargo_machete.yml @@ -9,7 +9,7 @@ jobs: steps: - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88 + toolchain: 1.92 - name: Machete install ## The official cargo-machete action uses: bnjbvr/cargo-machete@v0.9.1 diff --git a/.github/workflows/deploy_web_demo.yml b/.github/workflows/deploy_web_demo.yml index 21cfb159e..eb60d14fa 100644 --- a/.github/workflows/deploy_web_demo.yml +++ b/.github/workflows/deploy_web_demo.yml @@ -38,7 +38,7 @@ jobs: with: profile: minimal target: wasm32-unknown-unknown - toolchain: 1.88.0 + toolchain: 1.92.0 override: true - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/preview_build.yml b/.github/workflows/preview_build.yml index fe37eb8cb..22ea08cba 100644 --- a/.github/workflows/preview_build.yml +++ b/.github/workflows/preview_build.yml @@ -20,7 +20,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88.0 + toolchain: 1.92.0 targets: wasm32-unknown-unknown - uses: Swatinem/rust-cache@v2 with: diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f71588545..9018d251b 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -19,7 +19,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88.0 + toolchain: 1.92.0 - name: Install packages (Linux) if: runner.os == 'Linux' @@ -74,7 +74,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88.0 + toolchain: 1.92.0 targets: wasm32-unknown-unknown - run: sudo apt-get update && sudo apt-get install libgtk-3-dev libatk1.0-dev @@ -148,7 +148,7 @@ jobs: - uses: actions/checkout@v4 - uses: EmbarkStudios/cargo-deny-action@v2 with: - rust-version: "1.88.0" + rust-version: "1.92.0" log-level: error command: check arguments: --target ${{ matrix.target }} @@ -164,7 +164,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88.0 + toolchain: 1.92.0 targets: aarch64-linux-android - name: Set up cargo cache @@ -186,7 +186,7 @@ jobs: - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88.0 + toolchain: 1.92.0 targets: aarch64-apple-ios - name: Set up cargo cache @@ -206,7 +206,7 @@ jobs: - uses: actions/checkout@v4 - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88.0 + toolchain: 1.92.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 @@ -228,7 +228,7 @@ jobs: lfs: true - uses: dtolnay/rust-toolchain@stable with: - toolchain: 1.88.0 + toolchain: 1.92.0 - name: Set up cargo cache uses: Swatinem/rust-cache@v2 diff --git a/.typos.toml b/.typos.toml index 3ae860ad9..16659f4c7 100644 --- a/.typos.toml +++ b/.typos.toml @@ -5,8 +5,8 @@ [default.extend-words] ime = "ime" # Input Method Editor nknown = "nknown" # part of @55nknown username -isse = "isse" # part of @IsseW username -tye = "tye" # part of @tye-exe username +isse = "isse" # part of @IsseW username +tye = "tye" # part of @tye-exe username ro = "ro" # read-only, also part of the username @Phen-Ro typ = "typ" # Often used because `type` is a keyword in Rust diff --git a/Cargo.toml b/Cargo.toml index b291cb36c..470644bb4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,7 +23,7 @@ members = [ [workspace.package] edition = "2024" license = "MIT OR Apache-2.0" -rust-version = "1.88" +rust-version = "1.92" version = "0.33.3" @@ -184,7 +184,9 @@ branches_sharing_code = "warn" char_lit_as_u8 = "warn" checked_conversions = "warn" clear_with_drain = "warn" +clone_on_ref_ptr = "warn" cloned_instead_of_copied = "warn" +coerce_container_to_any = "warn" dbg_macro = "warn" debug_assert_with_mut_call = "warn" default_union_representation = "warn" @@ -194,7 +196,9 @@ disallowed_methods = "warn" # See clippy.toml disallowed_names = "warn" # See clippy.toml disallowed_script_idents = "warn" # See clippy.toml disallowed_types = "warn" # See clippy.toml +doc_broken_link = "warn" doc_comment_double_space_linebreaks = "warn" +doc_include_without_cfg = "warn" doc_link_with_quotes = "warn" doc_markdown = "warn" elidable_lifetime_names = "warn" @@ -227,6 +231,7 @@ inefficient_to_string = "warn" infinite_loop = "warn" into_iter_without_iter = "warn" invalid_upcast_comparisons = "warn" +ip_constant = "warn" iter_filter_is_ok = "warn" iter_filter_is_some = "warn" iter_not_returning_iterator = "warn" @@ -299,6 +304,7 @@ ref_patterns = "warn" rest_pat_in_fully_bound_structs = "warn" return_and_then = "warn" same_functions_in_if_condition = "warn" +self_only_used_in_recursion = "warn" semicolon_if_nothing_returned = "warn" set_contains_or_insert = "warn" single_char_pattern = "warn" @@ -310,7 +316,6 @@ string_add = "warn" string_add_assign = "warn" string_lit_as_bytes = "warn" string_lit_chars_any = "warn" -string_to_string = "warn" suspicious_command_arg_space = "warn" suspicious_xor_used_as_pow = "warn" todo = "warn" @@ -319,7 +324,7 @@ trailing_empty_array = "warn" trait_duplication_in_bounds = "warn" transmute_ptr_to_ptr = "warn" tuple_array_conversions = "warn" -unchecked_duration_subtraction = "warn" +unchecked_time_subtraction = "warn" undocumented_unsafe_blocks = "warn" unimplemented = "warn" uninhabited_references = "warn" @@ -339,6 +344,7 @@ unused_peekable = "warn" unused_rounding = "warn" unused_self = "warn" unused_trait_names = "warn" +unwrap_used = "warn" use_self = "warn" useless_let_if_seq = "warn" useless_transmute = "warn" @@ -348,10 +354,10 @@ zero_sized_map_values = "warn" # TODO(emilk): maybe enable more of these lints? +cast_possible_wrap = "allow" comparison_chain = "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 diff --git a/clippy.toml b/clippy.toml index 6602fbc6c..a57ed66fe 100644 --- a/clippy.toml +++ b/clippy.toml @@ -3,7 +3,7 @@ # ----------------------------------------------------------------------------- # Section identical to scripts/clippy_wasm/clippy.toml: -msrv = "1.88" +msrv = "1.92" allow-unwrap-in-tests = true @@ -39,9 +39,9 @@ disallowed-methods = [ # but we cannot disable them all here (because of e.g. https://github.com/rust-lang/rust-clippy/issues/10406) # so we do that in `clipppy_wasm.toml` instead. - { path = "std::env::temp_dir", readon = "Use the tempfile crate instead" }, + { path = "std::env::temp_dir", reason = "Use the tempfile crate instead" }, { path = "std::panic::catch_unwind", reason = "We compile with `panic = abort" }, - { path = "std::thread::spawn", readon = "Use `std::thread::Builder` and name the thread" }, + { path = "std::thread::spawn", reason = "Use `std::thread::Builder` and name the thread" }, ] # https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_names diff --git a/crates/ecolor/src/hsva.rs b/crates/ecolor/src/hsva.rs index 06adb121f..17008f5da 100644 --- a/crates/ecolor/src/hsva.rs +++ b/crates/ecolor/src/hsva.rs @@ -41,7 +41,7 @@ impl Hsva { /// From linear RGBA with premultiplied alpha #[inline] pub fn from_rgba_premultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { - #![allow(clippy::many_single_char_names)] + #![expect(clippy::many_single_char_names)] if a <= 0.0 { if r == 0.0 && b == 0.0 && a == 0.0 { Self::default() @@ -57,7 +57,7 @@ impl Hsva { /// From linear RGBA without premultiplied alpha #[inline] pub fn from_rgba_unmultiplied(r: f32, g: f32, b: f32, a: f32) -> Self { - #![allow(clippy::many_single_char_names)] + #![expect(clippy::many_single_char_names)] let (h, s, v) = hsv_from_rgb([r, g, b]); Self { h, s, v, a } } @@ -189,7 +189,7 @@ impl From for Hsva { /// All ranges in 0-1, rgb is linear. #[inline] pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) { - #![allow(clippy::many_single_char_names)] + #![expect(clippy::many_single_char_names)] let min = r.min(g.min(b)); let max = r.max(g.max(b)); // value @@ -213,7 +213,7 @@ pub fn hsv_from_rgb([r, g, b]: [f32; 3]) -> (f32, f32, f32) { /// All ranges in 0-1, rgb is linear. #[inline] pub fn rgb_from_hsv((h, s, v): (f32, f32, f32)) -> [f32; 3] { - #![allow(clippy::many_single_char_names)] + #![expect(clippy::many_single_char_names)] let h = (h.fract() + 1.0).fract(); // wrap let s = s.clamp(0.0, 1.0); diff --git a/crates/ecolor/src/lib.rs b/crates/ecolor/src/lib.rs index f4cdf4d77..ea7cff6f7 100644 --- a/crates/ecolor/src/lib.rs +++ b/crates/ecolor/src/lib.rs @@ -19,7 +19,7 @@ #![cfg_attr(feature = "document-features", doc = document_features::document_features!())] //! -#![allow(clippy::wrong_self_convention)] +#![expect(clippy::wrong_self_convention)] #[cfg(feature = "cint")] mod cint_impl; diff --git a/crates/eframe/Cargo.toml b/crates/eframe/Cargo.toml index 6924633f1..86f63c50e 100644 --- a/crates/eframe/Cargo.toml +++ b/crates/eframe/Cargo.toml @@ -251,7 +251,10 @@ web-sys = { workspace = true, features = [ ] } # optional web: -egui-wgpu = { workspace = true, optional = true, features = ["capture"] } # if wgpu is used, use it without (!) winit +egui-wgpu = { workspace = true, optional = true, features = [ + # if wgpu is used, use it without (!) winit: + "capture", +] } wgpu = { workspace = true, optional = true } # Native dev dependencies for testing diff --git a/crates/eframe/src/epi.rs b/crates/eframe/src/epi.rs index e7d64fcfb..c37dc1cf6 100644 --- a/crates/eframe/src/epi.rs +++ b/crates/eframe/src/epi.rs @@ -791,6 +791,7 @@ impl Frame { /// This function will take the ownership of your [`glow::Texture`], so please do not delete your [`glow::Texture`] after registering. #[cfg(all(feature = "glow", not(target_arch = "wasm32")))] pub fn register_native_glow_texture(&mut self, native: glow::Texture) -> egui::TextureId { + #[expect(clippy::unwrap_used)] self.glow_register_native_texture.as_mut().unwrap()(native) } diff --git a/crates/eframe/src/lib.rs b/crates/eframe/src/lib.rs index 252320462..151fb79ce 100644 --- a/crates/eframe/src/lib.rs +++ b/crates/eframe/src/lib.rs @@ -142,7 +142,6 @@ //! #![warn(missing_docs)] // let's keep eframe well-documented -#![allow(clippy::needless_doctest_main)] // Limitation imposed by `accesskit_winit`: // https://github.com/AccessKit/accesskit/tree/accesskit-v0.18.0/platforms/winit#android-activity-compatibility` @@ -253,7 +252,7 @@ pub mod icon_data; /// This function can fail if we fail to set up a graphics context. #[cfg(not(target_arch = "wasm32"))] #[cfg(any(feature = "glow", feature = "wgpu_no_default_features"))] -#[allow(clippy::needless_pass_by_value, clippy::allow_attributes)] +#[allow(clippy::allow_attributes, clippy::needless_pass_by_value)] pub fn run_native( app_name: &str, mut native_options: NativeOptions, diff --git a/crates/eframe/src/native/app_icon.rs b/crates/eframe/src/native/app_icon.rs index 233bf4c9d..3ac61d8e6 100644 --- a/crates/eframe/src/native/app_icon.rs +++ b/crates/eframe/src/native/app_icon.rs @@ -47,7 +47,7 @@ enum AppIconStatus { NotSetTryAgain, /// We successfully set the icon and it should be visible now. - #[allow(dead_code, clippy::allow_attributes)] // Not used on Linux + #[allow(clippy::allow_attributes, dead_code)] // Not used on Linux Set, } @@ -71,7 +71,7 @@ fn set_title_and_icon(_title: &str, _icon_data: Option<&IconData>) -> AppIconSta #[cfg(target_os = "macos")] return set_title_and_icon_mac(_title, _icon_data); - #[allow(unreachable_code, clippy::allow_attributes)] + #[allow(clippy::allow_attributes, unreachable_code)] AppIconStatus::NotSetIgnored } diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 6217aef43..96a52db88 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -376,6 +376,7 @@ impl EpiIntegration { fn load_default_egui_icon() -> egui::IconData { profiling::function_scope!(); + #[expect(clippy::unwrap_used)] crate::icon_data::from_png_bytes(&include_bytes!("../../data/icon.png")[..]).unwrap() } diff --git a/crates/eframe/src/native/glow_integration.rs b/crates/eframe/src/native/glow_integration.rs index 9306cf9cc..1cd49449f 100644 --- a/crates/eframe/src/native/glow_integration.rs +++ b/crates/eframe/src/native/glow_integration.rs @@ -5,7 +5,8 @@ //! There is a bunch of improvements we could do, //! like removing a bunch of `unwraps`. -#![allow(clippy::undocumented_unsafe_blocks)] +#![expect(clippy::undocumented_unsafe_blocks)] +#![expect(clippy::unwrap_used)] use std::{cell::RefCell, num::NonZeroU32, rc::Rc, sync::Arc, time::Instant}; @@ -216,7 +217,7 @@ impl<'app> GlowWinitApp<'app> { storage.as_deref(), &mut self.native_options, )?; - let gl = painter.gl().clone(); + let gl = Arc::clone(painter.gl()); let max_texture_side = painter.max_texture_side(); glutin.max_texture_side = Some(max_texture_side); @@ -234,9 +235,9 @@ impl<'app> GlowWinitApp<'app> { &self.app_name, &self.native_options, storage, - Some(gl.clone()), + Some(Arc::clone(&gl)), Some(Box::new({ - let painter = painter.clone(); + let painter = Rc::clone(&painter); move |native| painter.borrow_mut().register_native_texture(native) })), #[cfg(feature = "wgpu_no_default_features")] @@ -244,7 +245,7 @@ impl<'app> GlowWinitApp<'app> { ); { - let event_loop_proxy = self.repaint_proxy.clone(); + let event_loop_proxy = Arc::clone(&self.repaint_proxy); integration .egui_ctx .set_request_repaint_callback(move |info| { diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 7a3a8b4c4..0597d318c 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -349,8 +349,6 @@ pub fn run_glow( mut native_options: epi::NativeOptions, app_creator: epi::AppCreator<'_>, ) -> Result { - #![allow(clippy::needless_return_with_question_mark)] // False positive - use super::glow_integration::GlowWinitApp; #[cfg(not(target_os = "ios"))] @@ -387,8 +385,6 @@ pub fn run_wgpu( mut native_options: epi::NativeOptions, app_creator: epi::AppCreator<'_>, ) -> Result { - #![allow(clippy::needless_return_with_question_mark)] // False positive - use super::wgpu_integration::WgpuWinitApp; #[cfg(not(target_os = "ios"))] diff --git a/crates/eframe/src/native/wgpu_integration.rs b/crates/eframe/src/native/wgpu_integration.rs index b7708f6f6..cb634200a 100644 --- a/crates/eframe/src/native/wgpu_integration.rs +++ b/crates/eframe/src/native/wgpu_integration.rs @@ -219,7 +219,7 @@ impl<'app> WgpuWinitApp<'app> { { profiling::scope!("set_window"); - pollster::block_on(painter.set_window(ViewportId::ROOT, Some(window.clone())))?; + pollster::block_on(painter.set_window(ViewportId::ROOT, Some(Arc::clone(&window))))?; } let wgpu_render_state = painter.render_state(); @@ -238,7 +238,7 @@ impl<'app> WgpuWinitApp<'app> { ); { - let event_loop_proxy = self.repaint_proxy.clone(); + let event_loop_proxy = Arc::clone(&self.repaint_proxy); egui_ctx.set_request_repaint_callback(move |info| { log::trace!("request_repaint_callback: {info:?}"); @@ -256,7 +256,7 @@ impl<'app> WgpuWinitApp<'app> { }); } - #[allow(unused_mut, clippy::allow_attributes)] // used for accesskit + #[allow(clippy::allow_attributes, unused_mut)] // used for accesskit let mut egui_winit = egui_winit::State::new( egui_ctx.clone(), ViewportId::ROOT, @@ -610,7 +610,7 @@ impl WgpuWinitRunning<'_> { { profiling::scope!("set_window"); - pollster::block_on(painter.set_window(viewport_id, Some(window.clone())))?; + pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(window))))?; } let Some(egui_winit) = egui_winit.as_mut() else { @@ -919,7 +919,7 @@ impl Viewport { let window = Arc::new(window); if let Err(err) = - pollster::block_on(painter.set_window(viewport_id, Some(window.clone()))) + pollster::block_on(painter.set_window(viewport_id, Some(Arc::clone(&window)))) { log::error!("on set_window: viewport_id {viewport_id:?} {err}"); } @@ -1051,7 +1051,8 @@ fn render_immediate_viewport( { profiling::scope!("set_window"); - if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(window.clone()))) { + if let Err(err) = pollster::block_on(painter.set_window(ids.this, Some(Arc::clone(window)))) + { log::error!( "when rendering viewport_id={:?}, set_window Error {err}", ids.this diff --git a/crates/eframe/src/native/winit_integration.rs b/crates/eframe/src/native/winit_integration.rs index 2cf2e9019..012c22f8e 100644 --- a/crates/eframe/src/native/winit_integration.rs +++ b/crates/eframe/src/native/winit_integration.rs @@ -27,7 +27,10 @@ pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Contex egui_ctx.options_mut(|o| { // eframe supports multi-pass (Context::request_discard). - o.max_passes = 2.try_into().unwrap(); + #[expect(clippy::unwrap_used)] + { + o.max_passes = 2.try_into().unwrap(); + } }); let memory = crate::native::epi_integration::load_egui_memory(storage).unwrap_or_default(); diff --git a/crates/eframe/src/stopwatch.rs b/crates/eframe/src/stopwatch.rs index e6eabcbdd..b5f956c82 100644 --- a/crates/eframe/src/stopwatch.rs +++ b/crates/eframe/src/stopwatch.rs @@ -1,4 +1,4 @@ -#![allow(dead_code)] // not everything is used on wasm +#![allow(clippy::allow_attributes, dead_code)] // not used on all platforms use web_time::Instant; @@ -23,7 +23,7 @@ impl Stopwatch { } pub fn pause(&mut self) { - let start = self.start.take().unwrap(); + let start = self.start.take().expect("Stopwatch is not running"); let duration = start.elapsed(); self.total_time_ns += duration.as_nanos(); } diff --git a/crates/eframe/src/web/app_runner.rs b/crates/eframe/src/web/app_runner.rs index 83b2cb855..11654135d 100644 --- a/crates/eframe/src/web/app_runner.rs +++ b/crates/eframe/src/web/app_runner.rs @@ -1,3 +1,5 @@ +use std::sync::Arc; + use egui::{TexturesDelta, UserData, ViewportCommand}; use crate::{App, epi, web::web_painter::WebPainter}; @@ -5,14 +7,14 @@ use crate::{App, epi, web::web_painter::WebPainter}; use super::{NeedRepaint, now_sec, text_agent::TextAgent}; pub struct AppRunner { - #[allow(dead_code, clippy::allow_attributes)] + #[allow(clippy::allow_attributes, dead_code)] pub(crate) web_options: crate::WebOptions, pub(crate) frame: epi::Frame, egui_ctx: egui::Context, painter: Box, pub(crate) input: super::WebInput, app: Box, - pub(crate) needs_repaint: std::sync::Arc, + pub(crate) needs_repaint: Arc, last_save_time: f64, pub(crate) text_agent: TextAgent, @@ -63,7 +65,7 @@ impl AppRunner { canvas, &web_options, )?; - gl = Some(painter.gl().clone()); + gl = Some(Arc::clone(painter.gl())); Box::new(painter) as Box } @@ -138,10 +140,9 @@ impl AppRunner { wgpu_render_state, }; - let needs_repaint: std::sync::Arc = - std::sync::Arc::new(NeedRepaint::new(web_options.max_fps)); + let needs_repaint: Arc = Arc::new(NeedRepaint::new(web_options.max_fps)); { - let needs_repaint = needs_repaint.clone(); + let needs_repaint = Arc::clone(&needs_repaint); egui_ctx.set_request_repaint_callback(move |info| { needs_repaint.repaint_after(info.delay.as_secs_f64()); }); diff --git a/crates/eframe/src/web/events.rs b/crates/eframe/src/web/events.rs index 88bedab35..d77444563 100644 --- a/crates/eframe/src/web/events.rs +++ b/crates/eframe/src/web/events.rs @@ -137,25 +137,24 @@ fn install_keydown(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), J && !modifiers.command // When text agent is focused, it is responsible for handling input events && !runner.text_agent.has_focus() + && let Some(text) = text_from_keyboard_event(&event) { - if let Some(text) = text_from_keyboard_event(&event) { - let egui_event = egui::Event::Text(text); - let should_stop_propagation = - (runner.web_options.should_stop_propagation)(&egui_event); - let should_prevent_default = - (runner.web_options.should_prevent_default)(&egui_event); - runner.input.raw.events.push(egui_event); - runner.needs_repaint.repaint_asap(); + let egui_event = egui::Event::Text(text); + let should_stop_propagation = + (runner.web_options.should_stop_propagation)(&egui_event); + let should_prevent_default = + (runner.web_options.should_prevent_default)(&egui_event); + runner.input.raw.events.push(egui_event); + runner.needs_repaint.repaint_asap(); - // If this is indeed text, then prevent any other action. - if should_prevent_default { - event.prevent_default(); - } + // If this is indeed text, then prevent any other action. + if should_prevent_default { + event.prevent_default(); + } - // Use web options to tell if the event should be propagated to parent elements. - if should_stop_propagation { - event.stop_propagation(); - } + // Use web options to tell if the event should be propagated to parent elements. + if should_stop_propagation { + event.stop_propagation(); } } @@ -321,30 +320,28 @@ fn install_copy_cut_paste(runner_ref: &WebRunner, target: &EventTarget) -> Resul return; // The eframe app is not interested } - if let Some(data) = event.clipboard_data() { - if let Ok(text) = data.get_data("text") { - let text = text.replace("\r\n", "\n"); + if let Some(data) = event.clipboard_data() + && let Ok(text) = data.get_data("text") + { + let text = text.replace("\r\n", "\n"); - let mut should_stop_propagation = true; - let mut should_prevent_default = true; - if !text.is_empty() { - let egui_event = egui::Event::Paste(text); - should_stop_propagation = - (runner.web_options.should_stop_propagation)(&egui_event); - should_prevent_default = - (runner.web_options.should_prevent_default)(&egui_event); - runner.input.raw.events.push(egui_event); - runner.needs_repaint.repaint_asap(); - } + let mut should_stop_propagation = true; + let mut should_prevent_default = true; + if !text.is_empty() { + let egui_event = egui::Event::Paste(text); + should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event); + should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event); + runner.input.raw.events.push(egui_event); + runner.needs_repaint.repaint_asap(); + } - // Use web options to tell if the web event should be propagated to parent elements based on the egui event. - if should_stop_propagation { - event.stop_propagation(); - } + // Use web options to tell if the web event should be propagated to parent elements based on the egui event. + if should_stop_propagation { + event.stop_propagation(); + } - if should_prevent_default { - event.prevent_default(); - } + if should_prevent_default { + event.prevent_default(); } } })?; @@ -562,45 +559,44 @@ fn install_pointerup(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), if is_interested_in_pointer_event( runner, egui::pos2(event.client_x() as f32, event.client_y() as f32), - ) { - if let Some(button) = button_from_mouse_event(&event) { - let modifiers = runner.input.raw.modifiers; - let egui_event = egui::Event::PointerButton { - pos, - button, - pressed: false, - modifiers, - }; - let should_stop_propagation = - (runner.web_options.should_stop_propagation)(&egui_event); - let should_prevent_default = - (runner.web_options.should_prevent_default)(&egui_event); - runner.input.raw.events.push(egui_event); + ) && let Some(button) = button_from_mouse_event(&event) + { + let modifiers = runner.input.raw.modifiers; + let egui_event = egui::Event::PointerButton { + pos, + button, + pressed: false, + modifiers, + }; + let should_stop_propagation = + (runner.web_options.should_stop_propagation)(&egui_event); + let should_prevent_default = + (runner.web_options.should_prevent_default)(&egui_event); + runner.input.raw.events.push(egui_event); - // Previously on iOS, the canvas would not receive focus on - // any touch event, which resulted in the on-screen keyboard - // not working when focusing on a text field in an egui app. - // This attempts to fix that by forcing the focus on any - // click on the canvas. - runner.canvas().focus().ok(); + // Previously on iOS, the canvas would not receive focus on + // any touch event, which resulted in the on-screen keyboard + // not working when focusing on a text field in an egui app. + // This attempts to fix that by forcing the focus on any + // click on the canvas. + runner.canvas().focus().ok(); - // In Safari we are only allowed to do certain things - // (like playing audio, start a download, etc) - // on user action, such as a click. - // So we need to run the app logic here and now: - runner.logic(); + // In Safari we are only allowed to do certain things + // (like playing audio, start a download, etc) + // on user action, such as a click. + // So we need to run the app logic here and now: + runner.logic(); - // Make sure we paint the output of the above logic call asap: - runner.needs_repaint.repaint_asap(); + // Make sure we paint the output of the above logic call asap: + runner.needs_repaint.repaint_asap(); - if should_prevent_default { - event.prevent_default(); - } + if should_prevent_default { + event.prevent_default(); + } - // Use web options to tell if the web event should be propagated to parent elements based on the egui event. - if should_stop_propagation { - event.stop_propagation(); - } + // Use web options to tell if the web event should be propagated to parent elements based on the egui event. + if should_stop_propagation { + event.stop_propagation(); } } }, @@ -713,29 +709,27 @@ fn install_touchstart(runner_ref: &WebRunner, target: &EventTarget) -> Result<() fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> { runner_ref.add_event_listener(target, "touchmove", |event: web_sys::TouchEvent, runner| { - if let Some((pos, touch)) = primary_touch_pos(runner, &event) { - if is_interested_in_pointer_event( + if let Some((pos, touch)) = primary_touch_pos(runner, &event) + && is_interested_in_pointer_event( runner, egui::pos2(touch.client_x() as f32, touch.client_y() as f32), - ) { - let egui_event = egui::Event::PointerMoved(pos); - let should_stop_propagation = - (runner.web_options.should_stop_propagation)(&egui_event); - let should_prevent_default = - (runner.web_options.should_prevent_default)(&egui_event); - runner.input.raw.events.push(egui_event); + ) + { + let egui_event = egui::Event::PointerMoved(pos); + let should_stop_propagation = (runner.web_options.should_stop_propagation)(&egui_event); + let should_prevent_default = (runner.web_options.should_prevent_default)(&egui_event); + runner.input.raw.events.push(egui_event); - push_touches(runner, egui::TouchPhase::Move, &event); - runner.needs_repaint.repaint(); + push_touches(runner, egui::TouchPhase::Move, &event); + runner.needs_repaint.repaint(); - // Use web options to tell if the web event should be propagated to parent elements based on the egui event. - if should_stop_propagation { - event.stop_propagation(); - } + // Use web options to tell if the web event should be propagated to parent elements based on the egui event. + if should_stop_propagation { + event.stop_propagation(); + } - if should_prevent_default { - event.prevent_default(); - } + if should_prevent_default { + event.prevent_default(); } } }) @@ -743,50 +737,49 @@ fn install_touchmove(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), fn install_touchend(runner_ref: &WebRunner, target: &EventTarget) -> Result<(), JsValue> { runner_ref.add_event_listener(target, "touchend", |event: web_sys::TouchEvent, runner| { - if let Some((pos, touch)) = primary_touch_pos(runner, &event) { - if is_interested_in_pointer_event( + if let Some((pos, touch)) = primary_touch_pos(runner, &event) + && is_interested_in_pointer_event( runner, egui::pos2(touch.client_x() as f32, touch.client_y() as f32), - ) { - // First release mouse to click: - let mut should_stop_propagation = true; - let mut should_prevent_default = true; - let egui_event = egui::Event::PointerButton { - pos, - button: egui::PointerButton::Primary, - pressed: false, - modifiers: runner.input.raw.modifiers, - }; - should_stop_propagation &= - (runner.web_options.should_stop_propagation)(&egui_event); - should_prevent_default &= (runner.web_options.should_prevent_default)(&egui_event); - runner.input.raw.events.push(egui_event); - // Then remove hover effect: - should_stop_propagation &= - (runner.web_options.should_stop_propagation)(&egui::Event::PointerGone); - should_prevent_default &= - (runner.web_options.should_prevent_default)(&egui::Event::PointerGone); - runner.input.raw.events.push(egui::Event::PointerGone); + ) + { + // First release mouse to click: + let mut should_stop_propagation = true; + let mut should_prevent_default = true; + let egui_event = egui::Event::PointerButton { + pos, + button: egui::PointerButton::Primary, + pressed: false, + modifiers: runner.input.raw.modifiers, + }; + should_stop_propagation &= (runner.web_options.should_stop_propagation)(&egui_event); + should_prevent_default &= (runner.web_options.should_prevent_default)(&egui_event); + runner.input.raw.events.push(egui_event); + // Then remove hover effect: + should_stop_propagation &= + (runner.web_options.should_stop_propagation)(&egui::Event::PointerGone); + should_prevent_default &= + (runner.web_options.should_prevent_default)(&egui::Event::PointerGone); + runner.input.raw.events.push(egui::Event::PointerGone); - push_touches(runner, egui::TouchPhase::End, &event); + push_touches(runner, egui::TouchPhase::End, &event); - runner.needs_repaint.repaint_asap(); + runner.needs_repaint.repaint_asap(); - // Use web options to tell if the web event should be propagated to parent elements based on the egui event. - if should_stop_propagation { - event.stop_propagation(); - } + // Use web options to tell if the web event should be propagated to parent elements based on the egui event. + if should_stop_propagation { + event.stop_propagation(); + } - if should_prevent_default { - event.prevent_default(); - } + if should_prevent_default { + event.prevent_default(); + } - // Fix virtual keyboard IOS - // Need call focus at the same time of event - if runner.text_agent.has_focus() { - runner.text_agent.set_focus(false); - runner.text_agent.set_focus(true); - } + // Fix virtual keyboard IOS + // Need call focus at the same time of event + if runner.text_agent.has_focus() { + runner.text_agent.set_focus(false); + runner.text_agent.set_focus(true); } } }) diff --git a/crates/eframe/src/web/mod.rs b/crates/eframe/src/web/mod.rs index ac4c637db..1e54d7a84 100644 --- a/crates/eframe/src/web/mod.rs +++ b/crates/eframe/src/web/mod.rs @@ -1,6 +1,7 @@ //! [`egui`] bindings for web apps (compiling to WASM). -#![allow(clippy::missing_errors_doc)] // So many `-> Result<_, JsValue>` +#![expect(clippy::missing_errors_doc)] // So many `-> Result<_, JsValue>` +#![expect(clippy::unwrap_used)] // TODO(emilk): remove unwraps mod app_runner; mod backend; @@ -145,18 +146,18 @@ fn canvas_content_rect(canvas: &web_sys::HtmlCanvasElement) -> egui::Rect { ); // We need to subtract padding and border: - if let Some(window) = web_sys::window() { - if let Ok(Some(style)) = window.get_computed_style(canvas) { - let get_property = |name: &str| -> Option { - let property = style.get_property_value(name).ok()?; - property.trim_end_matches("px").parse::().ok() - }; + if let Some(window) = web_sys::window() + && let Ok(Some(style)) = window.get_computed_style(canvas) + { + let get_property = |name: &str| -> Option { + let property = style.get_property_value(name).ok()?; + property.trim_end_matches("px").parse::().ok() + }; - rect.min.x += get_property("padding-left").unwrap_or_default(); - rect.min.y += get_property("padding-top").unwrap_or_default(); - rect.max.x -= get_property("padding-right").unwrap_or_default(); - rect.max.y -= get_property("padding-bottom").unwrap_or_default(); - } + rect.min.x += get_property("padding-left").unwrap_or_default(); + rect.min.y += get_property("padding-top").unwrap_or_default(); + rect.max.x -= get_property("padding-right").unwrap_or_default(); + rect.max.y -= get_property("padding-bottom").unwrap_or_default(); } rect @@ -284,8 +285,8 @@ fn create_clipboard_item(mime: &str, bytes: &[u8]) -> Result) { - #![allow(clippy::match_same_arms)] + #![expect(clippy::match_same_arms)] if !self.enabled(record.metadata()) { return; @@ -110,7 +110,7 @@ mod console { /// * `tokio-1.24.1/src/runtime/runtime.rs` /// * `rerun/src/main.rs` /// * `core/src/ops/function.rs` -#[allow(dead_code, clippy::allow_attributes)] // only used on web and in tests +#[allow(clippy::allow_attributes, dead_code)] // only used on web and in tests fn shorten_file_path(file_path: &str) -> &str { if let Some(i) = file_path.rfind("/src/") { if let Some(prev_slash) = file_path[..i].rfind('/') { diff --git a/crates/eframe/src/web/web_painter_glow.rs b/crates/eframe/src/web/web_painter_glow.rs index e2fc4a6f2..470fa40d3 100644 --- a/crates/eframe/src/web/web_painter_glow.rs +++ b/crates/eframe/src/web/web_painter_glow.rs @@ -28,7 +28,7 @@ impl WebPainterGlow { let (gl, shader_prefix) = init_glow_context_from_canvas(&canvas, options.webgl_context_option)?; - #[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm + #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm let gl = std::sync::Arc::new(gl); let painter = egui_glow::Painter::new(gl, shader_prefix, None, options.dithering) @@ -91,7 +91,7 @@ impl WebPainter for WebPainterGlow { for data in data { events.push(Event::Screenshot { viewport_id: ViewportId::default(), - image: image.clone(), + image: Arc::clone(&image), user_data: data, }); } @@ -192,17 +192,13 @@ fn is_safari_and_webkit_gtk(gl: &web_sys::WebGlRenderingContext) -> bool { .get_extension("WEBGL_debug_renderer_info") .unwrap() .is_some() - { - if let Ok(renderer) = + && let Ok(renderer) = gl.get_parameter(web_sys::WebglDebugRendererInfo::UNMASKED_RENDERER_WEBGL) - { - if let Some(renderer) = renderer.as_string() { - if renderer.contains("Apple") { - return true; - } - } - } + && let Some(renderer) = renderer.as_string() + && renderer.contains("Apple") + { + true + } else { + false } - - false } diff --git a/crates/eframe/src/web/web_painter_wgpu.rs b/crates/eframe/src/web/web_painter_wgpu.rs index 387366e5a..264ce6adc 100644 --- a/crates/eframe/src/web/web_painter_wgpu.rs +++ b/crates/eframe/src/web/web_painter_wgpu.rs @@ -105,7 +105,7 @@ impl WebPainterWgpu { surface_configuration, depth_stencil_format, depth_texture_view: None, - on_surface_error: options.wgpu_options.on_surface_error.clone(), + on_surface_error: Arc::clone(&options.wgpu_options.on_surface_error) as _, screen_capture_state: None, capture_tx, capture_rx, @@ -282,14 +282,12 @@ impl WebPainter for WebPainterWgpu { let mut capture_buffer = None; - if capture { - if let Some(capture_state) = &mut self.screen_capture_state { - capture_buffer = Some(capture_state.copy_textures( - &render_state.device, - &output_frame, - &mut encoder, - )); - } + if capture && let Some(capture_state) = &mut self.screen_capture_state { + capture_buffer = Some(capture_state.copy_textures( + &render_state.device, + &output_frame, + &mut encoder, + )); } Some((output_frame, capture_buffer)) @@ -301,16 +299,16 @@ impl WebPainter for WebPainterWgpu { .submit(user_cmd_bufs.into_iter().chain([encoder.finish()])); if let Some((frame, capture_buffer)) = frame_and_capture_buffer { - if let Some(capture_buffer) = capture_buffer { - if let Some(capture_state) = &self.screen_capture_state { - capture_state.read_screen_rgba( - self.ctx.clone(), - capture_buffer, - capture_data, - self.capture_tx.clone(), - ViewportId::ROOT, - ); - } + if let Some(capture_buffer) = capture_buffer + && let Some(capture_state) = &self.screen_capture_state + { + capture_state.read_screen_rgba( + self.ctx.clone(), + capture_buffer, + capture_data, + self.capture_tx.clone(), + ViewportId::ROOT, + ); } frame.present(); @@ -336,7 +334,7 @@ impl WebPainter for WebPainterWgpu { events.push(Event::Screenshot { viewport_id, user_data: data, - image: screenshot.clone(), + image: Arc::clone(&screenshot), }); } } diff --git a/crates/egui-wgpu/src/capture.rs b/crates/egui-wgpu/src/capture.rs index cd42b838c..58407fdd6 100644 --- a/crates/egui-wgpu/src/capture.rs +++ b/crates/egui-wgpu/src/capture.rs @@ -126,7 +126,7 @@ impl CaptureState { // It would be more efficient to reuse the Buffer, e.g. via some kind of ring buffer, but // for most screenshot use cases this should be fine. When taking many screenshots (e.g. for a video) // it might make sense to revisit this and implement a more efficient solution. - #[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm + #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm let buffer = device.create_buffer(&wgpu::BufferDescriptor { label: Some("egui_screen_capture_buffer"), size: (self.padding.padded_bytes_per_row * self.texture.height()) as u64, @@ -186,9 +186,9 @@ impl CaptureState { tx: CaptureSender, viewport_id: ViewportId, ) { - #[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm + #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm let buffer = Arc::new(buffer); - let buffer_clone = buffer.clone(); + let buffer_clone = Arc::clone(&buffer); let buffer_slice = buffer_clone.slice(..); let format = self.texture.format(); let tex_extent = self.texture.size(); diff --git a/crates/egui-wgpu/src/lib.rs b/crates/egui-wgpu/src/lib.rs index d340526af..880ab8f4a 100644 --- a/crates/egui-wgpu/src/lib.rs +++ b/crates/egui-wgpu/src/lib.rs @@ -16,8 +16,6 @@ #![doc = document_features::document_features!()] //! -#![allow(unsafe_code)] - pub use wgpu; /// Low-level painting of [`egui`](https://github.com/emilk/egui) on [`wgpu`]. @@ -247,7 +245,7 @@ impl RenderState { // On wasm, depending on feature flags, wgpu objects may or may not implement sync. // It doesn't make sense to switch to Rc for that special usecase, so simply disable the lint. - #[allow(clippy::arc_with_non_send_sync, clippy::allow_attributes)] // For wasm + #[allow(clippy::allow_attributes, clippy::arc_with_non_send_sync)] // For wasm Ok(Self { adapter, #[cfg(not(target_arch = "wasm32"))] diff --git a/crates/egui-wgpu/src/renderer.rs b/crates/egui-wgpu/src/renderer.rs index 108aa31c3..d3d21f19c 100644 --- a/crates/egui-wgpu/src/renderer.rs +++ b/crates/egui-wgpu/src/renderer.rs @@ -1,4 +1,4 @@ -#![allow(unsafe_code)] +#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps use std::{borrow::Cow, num::NonZeroU64, ops::Range}; diff --git a/crates/egui-wgpu/src/setup.rs b/crates/egui-wgpu/src/setup.rs index bd587350e..0c3cb8c39 100644 --- a/crates/egui-wgpu/src/setup.rs +++ b/crates/egui-wgpu/src/setup.rs @@ -48,7 +48,7 @@ impl WgpuSetup { pub async fn new_instance(&self) -> wgpu::Instance { match self { Self::CreateNew(create_new) => { - #[allow(unused_mut, clippy::allow_attributes)] + #[allow(clippy::allow_attributes, unused_mut)] let mut backends = create_new.instance_descriptor.backends; // Don't try WebGPU if we're not in a secure context. @@ -134,7 +134,7 @@ impl Clone for WgpuSetupCreateNew { instance_descriptor: self.instance_descriptor.clone(), power_preference: self.power_preference, native_adapter_selector: self.native_adapter_selector.clone(), - device_descriptor: self.device_descriptor.clone(), + device_descriptor: Arc::clone(&self.device_descriptor), } } } diff --git a/crates/egui-wgpu/src/winit.rs b/crates/egui-wgpu/src/winit.rs index a35466493..167d10c79 100644 --- a/crates/egui-wgpu/src/winit.rs +++ b/crates/egui-wgpu/src/winit.rs @@ -1,5 +1,7 @@ -#![allow(clippy::missing_errors_doc)] -#![allow(clippy::undocumented_unsafe_blocks)] +#![expect(clippy::missing_errors_doc)] +#![expect(clippy::undocumented_unsafe_blocks)] +#![expect(clippy::unwrap_used)] // TODO(emilk): avoid unwraps +#![expect(unsafe_code)] use crate::{RenderState, SurfaceErrorAction, WgpuConfiguration, renderer}; use crate::{ @@ -629,7 +631,7 @@ impl Painter { events.push(Event::Screenshot { viewport_id, user_data: data, - image: screenshot.clone(), + image: Arc::clone(&screenshot), }); } } diff --git a/crates/egui-winit/src/clipboard.rs b/crates/egui-winit/src/clipboard.rs index fc7334388..75d0469ec 100644 --- a/crates/egui-winit/src/clipboard.rs +++ b/crates/egui-winit/src/clipboard.rs @@ -175,7 +175,7 @@ fn init_arboard() -> Option { fn init_smithay_clipboard( raw_display_handle: Option, ) -> Option { - #![allow(clippy::undocumented_unsafe_blocks)] + #![expect(clippy::undocumented_unsafe_blocks)] profiling::function_scope!(); diff --git a/crates/egui-winit/src/lib.rs b/crates/egui-winit/src/lib.rs index 6663ba40f..7cbaec624 100644 --- a/crates/egui-winit/src/lib.rs +++ b/crates/egui-winit/src/lib.rs @@ -7,7 +7,7 @@ #![cfg_attr(feature = "document-features", doc = document_features::document_features!())] //! -#![allow(clippy::manual_range_contains)] +#![expect(clippy::manual_range_contains)] #[cfg(feature = "accesskit")] pub use accesskit_winit; diff --git a/crates/egui-winit/src/safe_area.rs b/crates/egui-winit/src/safe_area.rs index d29d654a3..5f4a9f9cf 100644 --- a/crates/egui-winit/src/safe_area.rs +++ b/crates/egui-winit/src/safe_area.rs @@ -25,7 +25,7 @@ mod ios { let app = UIApplication::sharedApplication(main_thread_marker); - #[allow(unsafe_code)] + #[expect(unsafe_code)] unsafe { // Look for the first window scene that's in the foreground for scene in app.connectedScenes() { diff --git a/crates/egui/src/atomics/atoms.rs b/crates/egui/src/atomics/atoms.rs index 4b19c9e26..1db7c63c6 100644 --- a/crates/egui/src/atomics/atoms.rs +++ b/crates/egui/src/atomics/atoms.rs @@ -198,8 +198,7 @@ macro_rules! all_the_atoms { $($T: IntoAtoms<'a>),* { fn collect(self, _atoms: &mut Atoms<'a>) { - #[allow(clippy::allow_attributes)] - #[allow(non_snake_case)] + #[allow(clippy::allow_attributes, non_snake_case)] let ($($T),*) = self; $($T.collect(_atoms);)* } diff --git a/crates/egui/src/cache/cache_storage.rs b/crates/egui/src/cache/cache_storage.rs index d4c3c9aef..255eca2d5 100644 --- a/crates/egui/src/cache/cache_storage.rs +++ b/crates/egui/src/cache/cache_storage.rs @@ -28,6 +28,7 @@ pub struct CacheStorage { impl CacheStorage { pub fn cache(&mut self) -> &mut Cache { + #[expect(clippy::unwrap_used)] self.caches .entry(std::any::TypeId::of::()) .or_insert_with(|| Box::::default()) diff --git a/crates/egui/src/containers/area.rs b/crates/egui/src/containers/area.rs index 35afab35a..3b6d4006e 100644 --- a/crates/egui/src/containers/area.rs +++ b/crates/egui/src/containers/area.rs @@ -172,7 +172,7 @@ impl Area { /// Set the [`UiStackInfo`] of the area's [`Ui`]. /// - /// Default to [`UiStackInfo::new(UiKind::GenericArea)`]. + /// Default to [`UiStackInfo`] with kind [`UiKind::GenericArea`]. #[inline] pub fn info(mut self, info: UiStackInfo) -> Self { self.info = info; @@ -459,7 +459,7 @@ impl Area { state.pivot_pos = Some(new_pos); } state.pivot_pos.get_or_insert_with(|| { - default_pos.unwrap_or_else(|| automatic_area_position(ctx, layer_id)) + default_pos.unwrap_or_else(|| automatic_area_position(ctx, constrain_rect, layer_id)) }); state.interactable = interactable; @@ -523,6 +523,7 @@ impl Area { enabled, }, true, + Default::default(), ); // Used to prevent drift @@ -699,7 +700,7 @@ fn pointer_pressed_on_area(ctx: &Context, layer_id: LayerId) -> bool { } } -fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 { +fn automatic_area_position(ctx: &Context, constrain_rect: Rect, layer_id: LayerId) -> Pos2 { let mut existing: Vec = ctx.memory(|mem| { mem.areas() .visible_windows() @@ -710,13 +711,9 @@ fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 { }); existing.sort_by_key(|r| r.left().round() as i32); - // NOTE: for the benefit of the egui demo, we position the windows so they don't - // cover the side panels, which means we use `available_rect` here instead of `constrain_rect` or `screen_rect`. - let available_rect = ctx.globally_available_rect(); - let spacing = 16.0; - let left = available_rect.left() + spacing; - let top = available_rect.top() + spacing; + let left = constrain_rect.left() + spacing; + let top = constrain_rect.top() + spacing; if existing.is_empty() { return pos2(left, top); @@ -726,6 +723,7 @@ fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 { let mut column_bbs = vec![existing[0]]; for &rect in &existing { + #[expect(clippy::unwrap_used)] let current_column_bb = column_bbs.last_mut().unwrap(); if rect.left() < current_column_bb.right() { // same column @@ -750,14 +748,15 @@ fn automatic_area_position(ctx: &Context, layer_id: LayerId) -> Pos2 { // Find first column with some available space at the bottom of it: for col_bb in &column_bbs { - if col_bb.bottom() < available_rect.center().y { + if col_bb.bottom() < constrain_rect.center().y { return pos2(col_bb.left(), col_bb.bottom() + spacing); } } // Maybe we can fit a new column? + #[expect(clippy::unwrap_used)] let rightmost = column_bbs.last().unwrap().right(); - if rightmost + 200.0 < available_rect.right() { + if rightmost + 200.0 < constrain_rect.right() { return pos2(rightmost + spacing, top); } diff --git a/crates/egui/src/containers/frame.rs b/crates/egui/src/containers/frame.rs index e4de2ca48..d556f827e 100644 --- a/crates/egui/src/containers/frame.rs +++ b/crates/egui/src/containers/frame.rs @@ -336,7 +336,7 @@ impl Frame { impl Frame { /// How much extra space the frame uses up compared to the content. /// - /// [`Self::inner_margin`] + [`Self.stroke`]`.width` + [`Self::outer_margin`]. + /// [`Self::inner_margin`] + [`Self::stroke`]`.width` + [`Self::outer_margin`]. #[inline] pub fn total_margin(&self) -> MarginF32 { MarginF32::from(self.inner_margin) diff --git a/crates/egui/src/containers/menu.rs b/crates/egui/src/containers/menu.rs index 9af76d299..1bd5954c8 100644 --- a/crates/egui/src/containers/menu.rs +++ b/crates/egui/src/containers/menu.rs @@ -444,11 +444,8 @@ impl SubMenu { let mut menu_config = self.config.unwrap_or_else(|| parent_config.clone()); menu_config.bar = false; - let menu_root_response = ui - .ctx() - .read_response(menu_id) - // Since we are a child of that ui, this should always exist - .unwrap(); + #[expect(clippy::unwrap_used)] // Since we are a child of that ui, this should always exist + let menu_root_response = ui.ctx().read_response(menu_id).unwrap(); let hover_pos = ui.ctx().pointer_hover_pos(); diff --git a/crates/egui/src/containers/old_popup.rs b/crates/egui/src/containers/old_popup.rs index 3ddf77bf6..f8e7cc900 100644 --- a/crates/egui/src/containers/old_popup.rs +++ b/crates/egui/src/containers/old_popup.rs @@ -1,5 +1,5 @@ //! Old and deprecated API for popups. Use [`Popup`] instead. -#![allow(deprecated)] +#![expect(deprecated)] use crate::containers::tooltip::Tooltip; use crate::{ diff --git a/crates/egui/src/containers/panel.rs b/crates/egui/src/containers/panel.rs index 6ed34fd7c..6281e6b41 100644 --- a/crates/egui/src/containers/panel.rs +++ b/crates/egui/src/containers/panel.rs @@ -255,9 +255,7 @@ impl<'a> PanelSizer<'a> { let side = self.panel.side; let size_range = self.panel.size_range; - if is_resizing && pointer.is_some() { - let pointer = pointer.unwrap(); - + if is_resizing && let Some(pointer) = pointer { match side { PanelSide::Vertical(side) => { self.size = (pointer.x - side.side_x(self.panel_rect)).abs(); @@ -760,8 +758,10 @@ impl Panel { ctx: &Context, add_contents: Box R + 'c>, ) -> InnerResponse { + #![expect(deprecated)] + let side = self.side; - let available_rect = ctx.globally_available_rect(); + let available_rect = ctx.available_rect(); let mut panel_ui = Ui::new( ctx.clone(), self.id, @@ -809,9 +809,7 @@ impl Panel { let resize_id = self.id.with("__resize"); let resize_response = ui.ctx().read_response(resize_id); - if resize_response.is_some() { - let resize_response = resize_response.unwrap(); - + if let Some(resize_response) = resize_response { // NOTE(sharky98): The original code was initializing to // false first, but it doesn't seem necessary. let is_resizing = resize_response.dragged(); @@ -1044,6 +1042,7 @@ impl CentralPanel { } /// Show the panel at the top level. + #[deprecated = "Use show_inside() instead"] pub fn show( self, ctx: &Context, @@ -1058,6 +1057,8 @@ impl CentralPanel { ctx: &Context, add_contents: Box R + 'c>, ) -> InnerResponse { + #![expect(deprecated)] + let id = Id::new((ctx.viewport_id(), "central_panel")); let mut panel_ui = Ui::new( @@ -1065,7 +1066,7 @@ impl CentralPanel { id, UiBuilder::new() .layer_id(LayerId::background()) - .max_rect(ctx.globally_available_rect().round_ui()), + .max_rect(ctx.available_rect().round_ui()), ); panel_ui.set_clip_rect(ctx.content_rect()); diff --git a/crates/egui/src/containers/scroll_area.rs b/crates/egui/src/containers/scroll_area.rs index 6e6fb18b8..afe1239c7 100644 --- a/crates/egui/src/containers/scroll_area.rs +++ b/crates/egui/src/containers/scroll_area.rs @@ -1,6 +1,6 @@ //! See [`ScrollArea`] for docs. -#![allow(clippy::needless_range_loop)] +#![expect(clippy::needless_range_loop)] use std::ops::{Add, AddAssign, BitOr, BitOrAssign}; @@ -9,7 +9,8 @@ use epaint::Margin; use crate::{ Context, CursorIcon, Id, NumExt as _, Pos2, Rangef, Rect, Response, Sense, Ui, UiBuilder, - UiKind, UiStackInfo, Vec2, Vec2b, emath, epaint, lerp, pass_state, pos2, remap, remap_clamp, + UiKind, UiStackInfo, Vec2, Vec2b, WidgetInfo, emath, epaint, lerp, pass_state, pos2, remap, + remap_clamp, }; #[derive(Clone, Copy, Debug)] @@ -1241,46 +1242,17 @@ impl Prepared { continue; } + let interact_id = id.with(d); + // Margin on either side of the scroll bar: let inner_margin = show_factor * scroll_style.bar_inner_margin; let outer_margin = show_factor * scroll_style.bar_outer_margin; - // top/bottom of a horizontal scroll (d==0). - // left/rigth of a vertical scroll (d==1). - let mut cross = if scroll_style.floating { - // The bounding rect of a fully visible bar. - // When we hover this area, we should show the full bar: - let max_bar_rect = if d == 0 { - outer_rect.with_min_y(outer_rect.max.y - outer_margin - scroll_style.bar_width) - } else { - outer_rect.with_min_x(outer_rect.max.x - outer_margin - scroll_style.bar_width) - }; + // bottom of a horizontal scroll (d==0). + // right of a vertical scroll (d==1). + let mut max_cross = outer_rect.max[1 - d] - outer_margin; - let is_hovering_bar_area = is_hovering_outer_rect - && ui.rect_contains_pointer(max_bar_rect) - && !is_dragging_background - || state.scroll_bar_interaction[d]; - - let is_hovering_bar_area_t = ui - .ctx() - .animate_bool_responsive(id.with((d, "bar_hover")), is_hovering_bar_area); - - let width = show_factor - * lerp( - scroll_style.floating_width..=scroll_style.bar_width, - is_hovering_bar_area_t, - ); - - let max_cross = outer_rect.max[1 - d] - outer_margin; - let min_cross = max_cross - width; - Rangef::new(min_cross, max_cross) - } else { - let min_cross = inner_rect.max[1 - d] + inner_margin; - let max_cross = outer_rect.max[1 - d] - outer_margin; - Rangef::new(min_cross, max_cross) - }; - - if ui.clip_rect().max[1 - d] < cross.max + outer_margin { + if ui.clip_rect().max[1 - d] - outer_margin < max_cross { // 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, @@ -1290,21 +1262,59 @@ impl Prepared { // 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 = cross.max - cross.min; - cross.max = ui.clip_rect().max[1 - d] - outer_margin; - cross.min = cross.max - width; + max_cross = ui.clip_rect().max[1 - d] - outer_margin; } - let outer_scroll_bar_rect = if d == 0 { - Rect::from_min_max( - pos2(scroll_bar_rect.left(), cross.min), - pos2(scroll_bar_rect.right(), cross.max), - ) + let full_width = scroll_style.bar_width; + + // The bounding rect of a fully visible bar. + // When we hover this area, we should show the full bar: + let max_bar_rect = if d == 0 { + outer_rect.with_min_y(max_cross - full_width) } else { - Rect::from_min_max( - pos2(cross.min, scroll_bar_rect.top()), - pos2(cross.max, scroll_bar_rect.bottom()), - ) + outer_rect.with_min_x(max_cross - full_width) + }; + + let sense = if scroll_source.scroll_bar && ui.is_enabled() { + Sense::click_and_drag() + } else { + Sense::hover() + }; + + // We always sense interaction with the full width, even if we antimate it growing/shrinking. + // This is to present a more consistent target for our hit test code, + // and to avoid producing jitter in "thin widget" heuristics there. + // Also: it make sense to detect any hover where the scroll bar _will_ be. + let response = ui.interact(max_bar_rect, interact_id, sense); + + response.widget_info(|| WidgetInfo::new(crate::WidgetType::ScrollBar)); + + // top/bottom of a horizontal scroll (d==0). + // left/rigth of a vertical scroll (d==1). + let cross = if scroll_style.floating { + let is_hovering_bar_area = response.hovered() || state.scroll_bar_interaction[d]; + + let is_hovering_bar_area_t = ui + .ctx() + .animate_bool_responsive(id.with((d, "bar_hover")), is_hovering_bar_area); + + let width = show_factor + * lerp( + scroll_style.floating_width..=full_width, + is_hovering_bar_area_t, + ); + + let min_cross = max_cross - width; + Rangef::new(min_cross, max_cross) + } else { + let min_cross = inner_rect.max[1 - d] + inner_margin; + Rangef::new(min_cross, max_cross) + }; + + let outer_scroll_bar_rect = if d == 0 { + Rect::from_x_y_ranges(scroll_bar_rect.x_range(), cross) + } else { + Rect::from_x_y_ranges(cross, scroll_bar_rect.y_range()) }; let from_content = |content| { @@ -1344,14 +1354,6 @@ impl Prepared { let handle_rect = calculate_handle_rect(d, &state.offset); - let interact_id = id.with(d); - let sense = if scroll_source.scroll_bar && ui.is_enabled() { - Sense::click_and_drag() - } else { - Sense::hover() - }; - let response = ui.interact(outer_scroll_bar_rect, interact_id, sense); - state.scroll_bar_interaction[d] = response.hovered() || response.dragged(); if let Some(pointer_pos) = response.interact_pointer_pos() { diff --git a/crates/egui/src/containers/window.rs b/crates/egui/src/containers/window.rs index 2ea949e88..6dc7927ae 100644 --- a/crates/egui/src/containers/window.rs +++ b/crates/egui/src/containers/window.rs @@ -475,11 +475,11 @@ impl Window<'_> { fade_out, } = self; - let header_color = frame.map_or_else( - || ctx.global_style().visuals.widgets.open.weak_bg_fill, - |f| f.fill, - ); - let mut window_frame = frame.unwrap_or_else(|| Frame::window(&ctx.global_style())); + let style = ctx.global_style(); + + let header_color = + frame.map_or_else(|| style.visuals.widgets.open.weak_bg_fill, |f| f.fill); + let mut window_frame = frame.unwrap_or_else(|| Frame::window(&style)); let is_explicitly_closed = matches!(open, Some(false)); let is_open = !is_explicitly_closed || ctx.memory(|mem| mem.everything_is_visible()); @@ -511,7 +511,6 @@ impl Window<'_> { // Calculate roughly how much larger the full window inner size is compared to the content rect let (title_bar_height_with_margin, title_content_spacing) = if with_title_bar { - let style = ctx.global_style(); let title_bar_inner_height = ctx .fonts_mut(|fonts| title.font_height(fonts, &style)) .at_least(style.spacing.interact_size.y); @@ -542,7 +541,7 @@ impl Window<'_> { // First check for resize to avoid frame delay: let last_frame_outer_rect = area.state().rect(); - let resize_interaction = resize_interaction( + let resize_interaction = do_resize_interaction( ctx, possible, area.id(), @@ -623,6 +622,17 @@ impl Window<'_> { .map_or((None, None), |ir| (Some(ir.inner), Some(ir.response))); let outer_rect = frame.end(&mut area_content_ui).rect; + + // Do resize interaction _again_, to move their widget rectangles on TOP of the rest of the window. + let resize_interaction = do_resize_interaction( + ctx, + possible, + area.id(), + area_layer_id, + last_frame_outer_rect, + window_frame, + ); + paint_resize_corner( &area_content_ui, &possible, @@ -924,7 +934,7 @@ fn move_and_resize_window(ctx: &Context, id: Id, interaction: &ResizeInteraction Some(rect.round_ui()) } -fn resize_interaction( +fn do_resize_interaction( ctx: &Context, possible: PossibleInteractions, _accessibility_parent: Id, @@ -958,7 +968,16 @@ fn resize_interaction( enabled: true, }, true, + InteractOptions { + // We call this multiple times. + // First to read the result (to avoid frame delay) + // and the second time to move it to the top, above the window contents. + move_to_top: true, + }, ); + + response.widget_info(|| WidgetInfo::new(crate::WidgetType::ResizeHandle)); + SideResponse { hover: response.hovered(), drag: response.dragged(), @@ -967,8 +986,10 @@ fn resize_interaction( let id = Id::new(layer_id).with("edge_drag"); - let side_grab_radius = ctx.global_style().interaction.resize_grab_radius_side; - let corner_grab_radius = ctx.global_style().interaction.resize_grab_radius_corner; + let style = ctx.global_style(); + + let side_grab_radius = style.interaction.resize_grab_radius_side; + let corner_grab_radius = style.interaction.resize_grab_radius_corner; let vetrtical_rect = |a: Pos2, b: Pos2| { Rect::from_min_max(a, b).expand2(vec2(side_grab_radius, -corner_grab_radius)) @@ -1274,7 +1295,7 @@ impl TitleBar { let text_pos = text_pos - self.title_galley.rect.min.to_vec2(); ui.painter().galley( text_pos, - self.title_galley.clone(), + Arc::clone(&self.title_galley), ui.visuals().text_color(), ); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 67abb0556..f8996b8a3 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -19,8 +19,8 @@ use crate::{ ImmediateViewportRendererCallback, Key, KeyboardShortcut, Label, LayerId, Memory, ModifierNames, Modifiers, NumExt as _, Order, Painter, RawInput, Response, RichText, SafeAreaInsets, ScrollArea, Sense, Style, TextStyle, TextureHandle, TextureOptions, Ui, - ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair, ViewportIdSet, - ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText, + UiBuilder, ViewportBuilder, ViewportCommand, ViewportId, ViewportIdMap, ViewportIdPair, + ViewportIdSet, ViewportOutput, Visuals, Widget as _, WidgetRect, WidgetText, animation_manager::AnimationManager, containers::{self, area::AreaState}, data::output::PlatformOutput, @@ -584,8 +584,8 @@ impl ContextImpl { } } - fn accesskit_node_builder(&mut self, id: Id) -> &mut accesskit::Node { - let state = self.viewport().this_pass.accesskit_state.as_mut().unwrap(); + fn accesskit_node_builder(&mut self, id: Id) -> Option<&mut accesskit::Node> { + let state = self.viewport().this_pass.accesskit_state.as_mut()?; let builders = &mut state.nodes; if let std::collections::hash_map::Entry::Vacant(entry) = builders.entry(id) { @@ -611,11 +611,11 @@ impl ContextImpl { let parent_id = find_accesskit_parent(&state.parent_map, builders, id) .unwrap_or_else(crate::accesskit_root_id); - let parent_builder = builders.get_mut(&parent_id).unwrap(); + let parent_builder = builders.get_mut(&parent_id)?; parent_builder.push_child(id.accesskit_id()); } - builders.get_mut(&id).unwrap() + builders.get_mut(&id) } fn pixels_per_point(&mut self) -> f32 { @@ -793,11 +793,23 @@ impl Context { let plugins = self.read(|ctx| ctx.plugins.ordered_plugins()); #[expect(deprecated)] self.run(new_input, |ctx| { - crate::CentralPanel::no_frame().show(ctx, |ui| { - plugins.on_begin_pass(ui); - run_ui(ui); - plugins.on_end_pass(ui); - }); + let mut top_ui = Ui::new( + ctx.clone(), + Id::new((ctx.viewport_id(), "__top_ui")), + UiBuilder::new() + .layer_id(LayerId::background()) + .max_rect(ctx.available_rect().round_ui()), + ); + + { + plugins.on_begin_pass(&mut top_ui); + run_ui(&mut top_ui); + plugins.on_end_pass(&mut top_ui); + } + + // Inform ctx about what we actually used, so we can shrink the native window to fit. + // TODO(emilk): make better use of this somehow + ctx.pass_state_mut(|state| state.allocate_central_panel(top_ui.min_rect())); }) } @@ -1214,7 +1226,12 @@ impl Context { /// /// `allow_focus` should usually be true, unless you call this function multiple times with the /// same widget, then `allow_focus` should only be true once (like in [`Ui::new`] (true) and [`Ui::remember_min_rect`] (false)). - pub(crate) fn create_widget(&self, w: WidgetRect, allow_focus: bool) -> Response { + pub(crate) fn create_widget( + &self, + w: WidgetRect, + allow_focus: bool, + options: crate::InteractOptions, + ) -> Response { let interested_in_focus = w.enabled && w.sense.is_focusable() && self.memory(|mem| mem.allows_interaction(w.layer_id)); @@ -1226,7 +1243,7 @@ impl Context { // We add all widgets here, even non-interactive ones, // because we need this list not only for checking for blocking widgets, // but also to know when we have reached the widget we are checking for cover. - viewport.this_pass.widgets.insert(w.layer_id, w); + viewport.this_pass.widgets.insert(w.layer_id, w, options); if allow_focus && interested_in_focus { ctx.memory.interested_in_focus(w.id, w.layer_id); @@ -1242,7 +1259,7 @@ impl Context { self.check_for_id_clash(w.id, w.rect, "widget"); } - #[allow(clippy::let_and_return, clippy::allow_attributes)] + #[allow(clippy::allow_attributes, clippy::let_and_return)] let res = self.get_response(w); #[cfg(debug_assertions)] @@ -1947,7 +1964,7 @@ impl Context { pub fn add_plugin(&self, plugin: impl plugin::Plugin + 'static) { let handle = plugin::PluginHandle::new(plugin); - let added = self.write(|ctx| ctx.plugins.add(handle.clone())); + let added = self.write(|ctx| ctx.plugins.add(Arc::clone(&handle))); if added { handle.lock().dyn_plugin_mut().setup(self); @@ -2073,13 +2090,13 @@ impl Context { /// The currently active [`Style`] used by all subsequent popups, menus, etc. pub fn global_style(&self) -> Arc