@@ -176,12 +176,50 @@ impl ContextImpl {
176
176
177
177
/// Used to store each widgets [Id], [Rect] and [Sense] each frame.
178
178
/// Used to check for overlaps between widgets when handling events.
179
- struct WidgetRect {
180
- id : Id ,
181
- rect : Rect ,
182
- sense : Sense ,
179
+ #[ derive( Clone , Copy , Debug , PartialEq , Eq ) ]
180
+ pub struct WidgetRect {
181
+ /// Where the widget is.
182
+ pub rect : Rect ,
183
+
184
+ /// The globally unique widget id.
185
+ ///
186
+ /// For interactive widgets, this better be globally unique.
187
+ /// If not there will get weird bugs,
188
+ /// and also big red warning test on the screen in debug builds
189
+ /// (see [`Options::warn_on_id_clash`]).
190
+ ///
191
+ /// You can ensure globally unique ids using [`Ui::push_id`].
192
+ pub id : Id ,
193
+
194
+ /// How the widget responds to interaction.
195
+ pub sense : Sense ,
196
+ }
197
+
198
+ /// Stores the positions of all widgets generated during a single egui update/frame.
199
+ ///
200
+ /// Acgtually, only those that are on screen.
201
+ #[ derive( Default , Clone , PartialEq , Eq ) ]
202
+ pub struct WidgetRects {
203
+ /// All widgets, in painting order.
204
+ pub by_layer : HashMap < LayerId , Vec < WidgetRect > > ,
205
+ }
206
+
207
+ impl WidgetRects {
208
+ /// Clear the contents while retaining allocated memory.
209
+ pub fn clear ( & mut self ) {
210
+ for rects in self . by_layer . values_mut ( ) {
211
+ rects. clear ( ) ;
212
+ }
213
+ }
214
+
215
+ /// Insert the given widget rect in the given layer.
216
+ pub fn insert ( & mut self , layer_id : LayerId , widget_rect : WidgetRect ) {
217
+ self . by_layer . entry ( layer_id) . or_default ( ) . push ( widget_rect) ;
218
+ }
183
219
}
184
220
221
+ // ----------------------------------------------------------------------------
222
+
185
223
/// State stored per viewport
186
224
#[ derive( Default ) ]
187
225
struct ViewportState {
@@ -208,10 +246,10 @@ struct ViewportState {
208
246
used : bool ,
209
247
210
248
/// Written to during the frame.
211
- layer_rects_this_frame : HashMap < LayerId , Vec < WidgetRect > > ,
249
+ layer_rects_this_frame : WidgetRects ,
212
250
213
251
/// Read
214
- layer_rects_prev_frame : HashMap < LayerId , Vec < WidgetRect > > ,
252
+ layer_rects_prev_frame : WidgetRects ,
215
253
216
254
/// State related to repaint scheduling.
217
255
repaint : ViewportRepaintInfo ,
@@ -360,14 +398,6 @@ impl ContextImpl {
360
398
. native_pixels_per_point
361
399
. unwrap_or ( 1.0 ) ;
362
400
363
- {
364
- std:: mem:: swap (
365
- & mut viewport. layer_rects_prev_frame ,
366
- & mut viewport. layer_rects_this_frame ,
367
- ) ;
368
- viewport. layer_rects_this_frame . clear ( ) ;
369
- }
370
-
371
401
let all_viewport_ids: ViewportIdSet = self . all_viewport_ids ( ) ;
372
402
373
403
let viewport = self . viewports . entry ( self . viewport_id ( ) ) . or_default ( ) ;
@@ -607,12 +637,12 @@ impl Default for Context {
607
637
}
608
638
609
639
impl Context {
610
- // Do read-only (shared access) transaction on Context
640
+ /// Do read-only (shared access) transaction on Context
611
641
fn read < R > ( & self , reader : impl FnOnce ( & ContextImpl ) -> R ) -> R {
612
642
reader ( & self . 0 . read ( ) )
613
643
}
614
644
615
- // Do read-write (exclusive access) transaction on Context
645
+ /// Do read-write (exclusive access) transaction on Context
616
646
fn write < R > ( & self , writer : impl FnOnce ( & mut ContextImpl ) -> R ) -> R {
617
647
writer ( & mut self . 0 . write ( ) )
618
648
}
@@ -843,19 +873,21 @@ impl Context {
843
873
844
874
// it is ok to reuse the same ID for e.g. a frame around a widget,
845
875
// or to check for interaction with the same widget twice:
846
- if prev_rect. expand ( 0.1 ) . contains_rect ( new_rect)
847
- || new_rect. expand ( 0.1 ) . contains_rect ( prev_rect)
848
- {
876
+ let is_same_rect = prev_rect. expand ( 0.1 ) . contains_rect ( new_rect)
877
+ || new_rect. expand ( 0.1 ) . contains_rect ( prev_rect) ;
878
+ if is_same_rect {
849
879
return ;
850
880
}
851
881
852
882
let show_error = |widget_rect : Rect , text : String | {
883
+ let screen_rect = self . screen_rect ( ) ;
884
+
853
885
let text = format ! ( "🔥 {text}" ) ;
854
886
let color = self . style ( ) . visuals . error_fg_color ;
855
887
let painter = self . debug_painter ( ) ;
856
888
painter. rect_stroke ( widget_rect, 0.0 , ( 1.0 , color) ) ;
857
889
858
- let below = widget_rect. bottom ( ) + 32.0 < self . input ( |i| i . screen_rect . bottom ( ) ) ;
890
+ let below = widget_rect. bottom ( ) + 32.0 < screen_rect. bottom ( ) ;
859
891
860
892
let text_rect = if below {
861
893
painter. debug_text (
@@ -1780,7 +1812,24 @@ impl ContextImpl {
1780
1812
1781
1813
let shapes = viewport. graphics . drain ( self . memory . areas ( ) . order ( ) ) ;
1782
1814
1783
- if viewport. input . wants_repaint ( ) {
1815
+ let mut repaint_needed = false ;
1816
+
1817
+ {
1818
+ if self . memory . options . repaint_on_widget_change {
1819
+ crate :: profile_function!( "compare-widget-rects" ) ;
1820
+ if viewport. layer_rects_prev_frame != viewport. layer_rects_this_frame {
1821
+ repaint_needed = true ; // Some widget has moved
1822
+ }
1823
+ }
1824
+
1825
+ std:: mem:: swap (
1826
+ & mut viewport. layer_rects_prev_frame ,
1827
+ & mut viewport. layer_rects_this_frame ,
1828
+ ) ;
1829
+ viewport. layer_rects_this_frame . clear ( ) ;
1830
+ }
1831
+
1832
+ if repaint_needed || viewport. input . wants_repaint ( ) {
1784
1833
self . request_repaint ( ended_viewport_id) ;
1785
1834
}
1786
1835
@@ -2100,6 +2149,8 @@ impl Context {
2100
2149
///
2101
2150
/// Will return false if some other area is covering the given layer.
2102
2151
///
2152
+ /// The given rectangle is assumed to have been clipped by its parent clip rect.
2153
+ ///
2103
2154
/// See also [`Response::contains_pointer`].
2104
2155
pub fn rect_contains_pointer ( & self , layer_id : LayerId , rect : Rect ) -> bool {
2105
2156
if !rect. is_positive ( ) {
@@ -2129,6 +2180,8 @@ impl Context {
2129
2180
/// If another widget is covering us and is listening for the same input (click and/or drag),
2130
2181
/// this will return false.
2131
2182
///
2183
+ /// The given rectangle is assumed to have been clipped by its parent clip rect.
2184
+ ///
2132
2185
/// See also [`Response::contains_pointer`].
2133
2186
pub fn widget_contains_pointer (
2134
2187
& self ,
@@ -2137,6 +2190,10 @@ impl Context {
2137
2190
sense : Sense ,
2138
2191
rect : Rect ,
2139
2192
) -> bool {
2193
+ if !rect. is_positive ( ) {
2194
+ return false ; // don't even remember this widget
2195
+ }
2196
+
2140
2197
let contains_pointer = self . rect_contains_pointer ( layer_id, rect) ;
2141
2198
2142
2199
let mut blocking_widget = None ;
@@ -2146,19 +2203,17 @@ impl Context {
2146
2203
2147
2204
// We add all widgets here, even non-interactive ones,
2148
2205
// because we need this list not only for checking for blocking widgets,
2149
- // but also to know when we have reach the widget we are checking for cover.
2206
+ // but also to know when we have reached the widget we are checking for cover.
2150
2207
viewport
2151
2208
. layer_rects_this_frame
2152
- . entry ( layer_id)
2153
- . or_default ( )
2154
- . push ( WidgetRect { id, rect, sense } ) ;
2209
+ . insert ( layer_id, WidgetRect { id, rect, sense } ) ;
2155
2210
2156
2211
// Check if any other widget is covering us.
2157
2212
// Whichever widget is added LAST (=on top) gets the input.
2158
2213
if contains_pointer {
2159
2214
let pointer_pos = viewport. input . pointer . interact_pos ( ) ;
2160
2215
if let Some ( pointer_pos) = pointer_pos {
2161
- if let Some ( rects) = viewport. layer_rects_prev_frame . get ( & layer_id) {
2216
+ if let Some ( rects) = viewport. layer_rects_prev_frame . by_layer . get ( & layer_id) {
2162
2217
for blocking in rects. iter ( ) . rev ( ) {
2163
2218
if blocking. id == id {
2164
2219
// There are no earlier widgets before this one,
@@ -2293,25 +2348,14 @@ impl Context {
2293
2348
impl Context {
2294
2349
/// Show a ui for settings (style and tessellation options).
2295
2350
pub fn settings_ui ( & self , ui : & mut Ui ) {
2296
- use crate :: containers:: * ;
2351
+ let prev_options = self . options ( |o| o. clone ( ) ) ;
2352
+ let mut options = prev_options. clone ( ) ;
2297
2353
2298
- CollapsingHeader :: new ( "🎑 Style" )
2299
- . default_open ( true )
2300
- . show ( ui, |ui| {
2301
- self . style_ui ( ui) ;
2302
- } ) ;
2354
+ options. ui ( ui) ;
2303
2355
2304
- CollapsingHeader :: new ( "✒ Painting" )
2305
- . default_open ( true )
2306
- . show ( ui, |ui| {
2307
- let prev_tessellation_options = self . tessellation_options ( |o| * o) ;
2308
- let mut tessellation_options = prev_tessellation_options;
2309
- tessellation_options. ui ( ui) ;
2310
- ui. vertical_centered ( |ui| reset_button ( ui, & mut tessellation_options) ) ;
2311
- if tessellation_options != prev_tessellation_options {
2312
- self . tessellation_options_mut ( move |o| * o = tessellation_options) ;
2313
- }
2314
- } ) ;
2356
+ if options != prev_options {
2357
+ self . options_mut ( move |o| * o = options) ;
2358
+ }
2315
2359
}
2316
2360
2317
2361
/// Show the state of egui, including its input and output.
0 commit comments