From a1af9abe70ad3a1c049d8d9968886af483af666a Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 24 Mar 2026 15:46:56 +0100 Subject: [PATCH] Shrink the byte-size of `Response` slightly (#8011) Small optimization! 96 -> 88 bytes, so no huge win. --- crates/egui/src/atomics/atom_layout.rs | 5 ++-- crates/egui/src/context.rs | 15 ++++------ crates/egui/src/response.rs | 41 ++++++++++++++++++++++---- crates/egui/src/ui.rs | 2 +- crates/egui/src/widgets/label.rs | 4 +-- crates/emath/src/pos2.rs | 5 ++++ tests/egui_tests/tests/test_atoms.rs | 8 ++--- 7 files changed, 57 insertions(+), 23 deletions(-) diff --git a/crates/egui/src/atomics/atom_layout.rs b/crates/egui/src/atomics/atom_layout.rs index c408146d6..7894273f3 100644 --- a/crates/egui/src/atomics/atom_layout.rs +++ b/crates/egui/src/atomics/atom_layout.rs @@ -318,8 +318,9 @@ impl<'a> AtomLayout<'a> { let (_, rect) = ui.allocate_space(frame_size); let mut response = ui.interact(rect, id, sense); - response.intrinsic_size = - Some((Vec2::new(intrinsic_width, intrinsic_height) + margin.sum()).at_least(min_size)); + response.set_intrinsic_size( + (Vec2::new(intrinsic_width, intrinsic_height) + margin.sum()).at_least(min_size), + ); AllocatedAtomLayout { sized_atoms: sized_items, diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index fbc189132..af6c0e6d0 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -1378,8 +1378,8 @@ impl Context { interact_rect, sense, flags: Flags::empty(), - interact_pointer_pos: None, - intrinsic_size: None, + interact_pointer_pos_or_nan: Pos2::NAN, + intrinsic_size_or_nan: Vec2::NAN, }; res.flags.set(Flags::ENABLED, enabled); @@ -1470,14 +1470,11 @@ impl Context { || res.long_touched() || clicked || res.drag_stopped(); - if is_interacted_with { - res.interact_pointer_pos = input.pointer.interact_pos(); - if let (Some(to_global), Some(pos)) = ( - memory.to_global.get(&res.layer_id), - &mut res.interact_pointer_pos, - ) { - *pos = to_global.inverse() * *pos; + if is_interacted_with && let Some(mut pos) = input.pointer.interact_pos() { + if let Some(to_global) = memory.to_global.get(&res.layer_id) { + pos = to_global.inverse() * pos; } + res.interact_pointer_pos_or_nan = pos; } if input.pointer.any_down() && !is_interacted_with { diff --git a/crates/egui/src/response.rs b/crates/egui/src/response.rs index 495d5dcdf..241f5a381 100644 --- a/crates/egui/src/response.rs +++ b/crates/egui/src/response.rs @@ -55,7 +55,7 @@ pub struct Response { /// Where the pointer (mouse/touch) were when this widget was clicked or dragged. /// `None` if the widget is not being interacted with. #[doc(hidden)] - pub interact_pointer_pos: Option, + pub interact_pointer_pos_or_nan: Pos2, /// The intrinsic / desired size of the widget. /// @@ -67,12 +67,22 @@ pub struct Response { /// At the time of writing, this is only used by external crates /// for improved layouting. /// See for instance [`egui_flex`](https://github.com/lucasmerlin/hello_egui/tree/main/crates/egui_flex). - pub intrinsic_size: Option, + #[doc(hidden)] + pub intrinsic_size_or_nan: Vec2, #[doc(hidden)] pub flags: Flags, } +#[test] +fn test_response_size() { + assert_eq!( + std::mem::size_of::(), + 88, + "Keep Response small, because we create them often, and we want to keep it lean and fast" + ); +} + /// A bit set for various boolean properties of `Response`. #[doc(hidden)] #[derive(Copy, Clone, Debug)] @@ -489,7 +499,26 @@ impl Response { /// `None` if the widget is not being interacted with. #[inline] pub fn interact_pointer_pos(&self) -> Option { - self.interact_pointer_pos + let pos = self.interact_pointer_pos_or_nan; + if pos.any_nan() { None } else { Some(pos) } + } + + /// The intrinsic / desired size of the widget. + /// + /// This is the size that a non-wrapped, non-truncated, non-justified version of the widget + /// would have. + /// + /// If this is `None`, use [`Self::rect`] instead. + #[inline] + pub fn intrinsic_size(&self) -> Option { + let size = self.intrinsic_size_or_nan; + if size.any_nan() { None } else { Some(size) } + } + + /// Set the intrinsic / desired size of the widget. + #[inline] + pub fn set_intrinsic_size(&mut self, size: Vec2) { + self.intrinsic_size_or_nan = size; } /// If it is a good idea to show a tooltip, where is pointer? @@ -1007,8 +1036,10 @@ impl Response { interact_rect: self.interact_rect.union(other.interact_rect), sense: self.sense.union(other.sense), flags: self.flags | other.flags, - interact_pointer_pos: self.interact_pointer_pos.or(other.interact_pointer_pos), - intrinsic_size: None, + interact_pointer_pos_or_nan: self + .interact_pointer_pos() + .unwrap_or(other.interact_pointer_pos_or_nan), + intrinsic_size_or_nan: Vec2::NAN, } } } diff --git a/crates/egui/src/ui.rs b/crates/egui/src/ui.rs index daabd10b0..f0b270951 100644 --- a/crates/egui/src/ui.rs +++ b/crates/egui/src/ui.rs @@ -1281,7 +1281,7 @@ impl Ui { pub fn allocate_response(&mut self, desired_size: Vec2, sense: Sense) -> Response { let (id, rect) = self.allocate_space(desired_size); let mut response = self.interact(rect, id, sense); - response.intrinsic_size = Some(desired_size); + response.set_intrinsic_size(desired_size); response } diff --git a/crates/egui/src/widgets/label.rs b/crates/egui/src/widgets/label.rs index 284cfd12c..7b2d3a3ba 100644 --- a/crates/egui/src/widgets/label.rs +++ b/crates/egui/src/widgets/label.rs @@ -220,7 +220,7 @@ impl Label { .rect_without_leading_space() .translate(pos.to_vec2()); let mut response = ui.allocate_rect(rect, sense); - response.intrinsic_size = Some(galley.intrinsic_size()); + response.set_intrinsic_size(galley.intrinsic_size()); for placed_row in galley.rows.iter().skip(1) { let rect = placed_row.rect().translate(pos.to_vec2()); response |= ui.allocate_rect(rect, sense); @@ -256,7 +256,7 @@ impl Label { let galley = ui.fonts_mut(|fonts| fonts.layout_job(layout_job)); let (rect, mut response) = ui.allocate_exact_size(galley.size(), sense); - response.intrinsic_size = Some(galley.intrinsic_size()); + response.set_intrinsic_size(galley.intrinsic_size()); let galley_pos = match galley.job.halign { Align::LEFT => rect.left_top(), Align::Center => rect.center_top(), diff --git a/crates/emath/src/pos2.rs b/crates/emath/src/pos2.rs index fc26686b3..f67767e6b 100644 --- a/crates/emath/src/pos2.rs +++ b/crates/emath/src/pos2.rs @@ -119,6 +119,11 @@ impl Pos2 { /// Same as `Pos2::default()`. pub const ZERO: Self = Self { x: 0.0, y: 0.0 }; + pub const NAN: Self = Self { + x: f32::NAN, + y: f32::NAN, + }; + #[inline(always)] pub const fn new(x: f32, y: f32) -> Self { Self { x, y } diff --git a/tests/egui_tests/tests/test_atoms.rs b/tests/egui_tests/tests/test_atoms.rs index f6e9df14a..f7e0a4af1 100644 --- a/tests/egui_tests/tests/test_atoms.rs +++ b/tests/egui_tests/tests/test_atoms.rs @@ -92,17 +92,17 @@ fn test_intrinsic_size() { if let Some(current_intrinsic_size) = intrinsic_size { assert_eq!( Some(current_intrinsic_size), - response.intrinsic_size, + response.intrinsic_size(), "For wrapping: {wrapping:?}" ); } assert!( - response.intrinsic_size.is_some(), + response.intrinsic_size().is_some(), "intrinsic_size should be set for `Button`" ); - intrinsic_size = response.intrinsic_size; + intrinsic_size = response.intrinsic_size(); if wrapping == TextWrapMode::Extend { - assert_eq!(Some(response.rect.size()), response.intrinsic_size); + assert_eq!(Some(response.rect.size()), response.intrinsic_size()); } }); }