Skip to content

Commit

Permalink
Fix area click handling ignoring the modal backdrop
Browse files Browse the repository at this point in the history
  • Loading branch information
lucasmerlin committed Nov 28, 2024
1 parent 69125e5 commit 8ccd937
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 13 deletions.
34 changes: 24 additions & 10 deletions crates/egui/src/memory/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,15 @@ impl Memory {

/// Top-most layer at the given position.
pub fn layer_id_at(&self, pos: Pos2) -> Option<LayerId> {
self.areas().layer_id_at(pos, &self.layer_transforms)
self.areas()
.layer_id_at(pos, &self.layer_transforms)
.and_then(|layer_id| {
if self.is_above_modal_layer(layer_id) {
Some(layer_id)
} else {
self.top_modal_layer()
}
})
}

/// An iterator over all layers. Back-to-front, top is last.
Expand Down Expand Up @@ -893,20 +901,26 @@ impl Memory {
}
}

/// Returns true if
/// - this layer is the top-most modal layer or above it
/// - there is no modal layer
pub fn is_above_modal_layer(&self, layer_id: LayerId) -> bool {
if let Some(modal_layer) = self.focus().and_then(|f| f.top_modal_layer) {
matches!(
self.areas().compare_order(layer_id, modal_layer),
std::cmp::Ordering::Equal | std::cmp::Ordering::Greater
)
} else {
true
}
}

/// Does this layer allow interaction?
/// Returns true if
/// - the layer is not behind a modal layer
/// - the [`Order`] allows interaction
pub fn allows_interaction(&self, layer_id: LayerId) -> bool {
let is_above_modal_layer =
if let Some(modal_layer) = self.focus().and_then(|f| f.top_modal_layer) {
matches!(
self.areas().compare_order(layer_id, modal_layer),
std::cmp::Ordering::Equal | std::cmp::Ordering::Greater
)
} else {
true
};
let is_above_modal_layer = self.is_above_modal_layer(layer_id);
let ordering_allows_interaction = layer_id.order.allow_interaction();
is_above_modal_layer && ordering_allows_interaction
}
Expand Down
35 changes: 32 additions & 3 deletions crates/egui_demo_lib/src/demo/modals.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use egui::{Align, ComboBox, Context, Id, Layout, Modal, ProgressBar, Ui, Widget, Window};
use egui::{ComboBox, Context, Id, Modal, ProgressBar, Ui, Widget, Window};

#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
#[cfg_attr(feature = "serde", serde(default))]
Expand Down Expand Up @@ -90,7 +90,7 @@ impl crate::View for Modals {

egui::Sides::new().show(
ui,
|ui| {},
|_ui| {},
|ui| {
if ui.button("Save").clicked() {
*save_modal_open = true;
Expand All @@ -116,7 +116,7 @@ impl crate::View for Modals {

egui::Sides::new().show(
ui,
|ui| {},
|_ui| {},
|ui| {
if ui.button("Yes Please").clicked() {
*save_progress = Some(0.0);
Expand Down Expand Up @@ -255,4 +255,33 @@ mod tests {
result.unwrap();
}
}

// This tests whether the backdrop actually prevents interaction with lower layers.
#[test]
fn backdrop_should_prevent_focusing_lower_area() {
let initial_state = Modals {
save_modal_open: true,
save_progress: Some(0.0),
..Modals::default()
};

let mut harness = Harness::new_state(
|ctx, modals| {
modals.show(ctx, &mut true);
},
initial_state,
);

// TODO(lucasmerlin): Remove these extra runs once run checks for repaint requests
harness.run();
harness.run();
harness.run();

harness.get_by_label("Yes Please").simulate_click();

harness.run();

// This snapshots should show the progress bar modal on top of the save modal.
harness.wgpu_snapshot("modals_backdrop_should_prevent_focusing_lower_area");
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 8ccd937

Please sign in to comment.