Skip to main content

egui_elements/
overlay.rs

1//! Full-screen dimming used when stacking windows or modals.
2//!
3//! Call [`OverlayManager::window_opened`] / [`OverlayManager::window_closed`]
4//! when toggling a window, then [`OverlayManager::paint_overlay`] each frame.
5
6use egui::{Color32, Context, Id, LayerId, Order, Rect};
7use std::sync::{Arc, RwLock};
8
9/// Counts open windows and paints a darkening overlay.
10///
11/// Cheap to clone (`Arc`). Stored on [`crate::theme::Theme::overlay_manager`].
12#[derive(Clone, Debug, Default)]
13pub struct OverlayManager(Arc<RwLock<OverlayCounter>>);
14
15impl OverlayManager {
16   /// Create a manager with no open windows.
17   pub fn new() -> Self {
18      Self(Arc::new(RwLock::new(OverlayCounter::new())))
19   }
20
21   /// Lightest preset tint.
22   pub fn tint_0(&self) -> Color32 {
23      Color32::from_black_alpha(40)
24   }
25
26   /// Light preset tint.
27   pub fn tint_1(&self) -> Color32 {
28      Color32::from_black_alpha(60)
29   }
30
31   /// Medium preset tint.
32   pub fn tint_2(&self) -> Color32 {
33      Color32::from_black_alpha(80)
34   }
35
36   /// Darkest preset tint.
37   pub fn tint_3(&self) -> Color32 {
38      Color32::from_black_alpha(100)
39   }
40
41   /// Number of currently tracked open windows.
42   pub fn counter(&self) -> u8 {
43      self.0.read().unwrap().counter()
44   }
45
46   /// Last paint layer selected via `paint_*`.
47   pub fn order(&self) -> Order {
48      self.0.read().unwrap().order()
49   }
50
51   /// Paint the next overlay on the background layer.
52   pub fn paint_background(&self) {
53      self.0.write().unwrap().paint_background()
54   }
55
56   /// Paint the next overlay on the middle layer.
57   pub fn paint_middle(&self) {
58      self.0.write().unwrap().paint_middle()
59   }
60
61   /// Paint the next overlay on the foreground layer.
62   pub fn paint_foreground(&self) {
63      self.0.write().unwrap().paint_foreground()
64   }
65
66   /// Paint the next overlay on the tooltip layer.
67   pub fn paint_tooltip(&self) {
68      self.0.write().unwrap().paint_tooltip()
69   }
70
71   /// Paint the next overlay on the debug layer.
72   pub fn paint_debug(&self) {
73      self.0.write().unwrap().paint_debug()
74   }
75
76   /// Call this when you open a window
77   pub fn window_opened(&self) {
78      self.0.write().unwrap().window_opened();
79   }
80
81   /// Call this when you close a window
82   pub fn window_closed(&self) {
83      self.0.write().unwrap().window_closed();
84   }
85
86   /// Layer suggested by the current window count.
87   pub fn recommended_order(&self) -> Order {
88      self.0.read().unwrap().recommended_order()
89   }
90
91   /// Alpha derived from the window count (0 if none are open).
92   pub fn calculate_alpha(&self) -> u8 {
93      self.0.read().unwrap().calculate_alpha()
94   }
95
96   /// Returns the tint color based on the counter
97   pub fn overlay_tint(&self) -> Color32 {
98      self.0.read().unwrap().overlay_tint()
99   }
100
101   /// Paints a full-screen darkening overlay up to Foreground layer if needed
102   ///
103   /// If `recommend_order` is true, it will choose an order based on the counter
104   pub fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
105      self.0.read().unwrap().paint_overlay(ctx, recommend_order);
106   }
107
108   /// Paints an overlay at a specific screen position
109   pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
110      self.0.read().unwrap().paint_overlay_at(ctx, rect, order, id, tint);
111   }
112}
113
114#[derive(Clone, Debug)]
115struct OverlayCounter {
116   counter: u8,
117   order: Order,
118}
119
120impl Default for OverlayCounter {
121   fn default() -> Self {
122      Self::new()
123   }
124}
125
126impl OverlayCounter {
127   pub fn new() -> Self {
128      Self {
129         counter: 0,
130         order: Order::Background,
131      }
132   }
133
134   pub fn counter(&self) -> u8 {
135      self.counter
136   }
137
138   pub fn order(&self) -> Order {
139      self.order
140   }
141
142   fn paint_background(&mut self) {
143      self.order = Order::Background;
144   }
145
146   fn paint_middle(&mut self) {
147      self.order = Order::Middle;
148   }
149
150   fn paint_foreground(&mut self) {
151      self.order = Order::Foreground;
152   }
153
154   fn paint_tooltip(&mut self) {
155      self.order = Order::Tooltip;
156   }
157
158   fn paint_debug(&mut self) {
159      self.order = Order::Debug;
160   }
161
162   fn window_opened(&mut self) {
163      self.counter += 1;
164   }
165
166   fn window_closed(&mut self) {
167      if self.counter > 0 {
168         self.counter -= 1;
169      }
170   }
171
172   fn calculate_alpha(&self) -> u8 {
173      let counter = self.counter;
174
175      if counter == 0 {
176         return 0;
177      }
178
179      if counter > 3 {
180         return 220;
181      }
182
183      let mut a = 80;
184      for _ in 1..counter {
185         a += 40;
186      }
187
188      a
189   }
190
191   fn overlay_tint(&self) -> Color32 {
192      let counter = self.counter();
193
194      if counter == 1 {
195         return Color32::from_black_alpha(80);
196      }
197
198      let alpha = self.calculate_alpha();
199      Color32::from_black_alpha(alpha)
200   }
201
202   fn recommended_order(&self) -> Order {
203      if self.counter() == 1 {
204         Order::Background
205      } else if self.counter() == 2 {
206         Order::Middle
207      } else {
208         Order::Foreground
209      }
210   }
211
212   fn paint_overlay(&self, ctx: &Context, recommend_order: bool) {
213      let counter = self.counter();
214      if counter == 0 {
215         return;
216      }
217
218      let order = if recommend_order {
219         self.recommended_order()
220      } else {
221         self.order()
222      };
223
224      let layer_id = LayerId::new(order, Id::new("darkening_overlay"));
225
226      let painter = ctx.layer_painter(layer_id);
227      painter.rect_filled(ctx.content_rect(), 0.0, self.overlay_tint());
228   }
229
230   pub fn paint_overlay_at(&self, ctx: &Context, rect: Rect, order: Order, id: Id, tint: Color32) {
231      let layer_id = LayerId::new(order, id);
232
233      let painter = ctx.layer_painter(layer_id);
234      painter.rect_filled(rect, 0.0, tint);
235   }
236}