Skip to content

Commit

Permalink
Have non-resizable Areas ignore mouse input just beyond their bounds (#…
Browse files Browse the repository at this point in the history
…3039)

As described in #576, the method `Memory::layer_id_at` expands the
hit-testing region for every `Area` slightly. This is necessary so that,
when the user clicks to resize a `Window`, the mouse input isn't routed
to a different `Window`.

For non-resizable `Area`s (such as dropdown menus, context menus, date
pickers, and any non-resizable `Window`), this causes them to be
surrounded by a "dead zone" where any underlying widgets can't be
hovered or clicked. The effect is particularly noticeable in menu bars.

This commit adds a persisted `edges_padded_for_resize` property to
`Area`, which is `true` when the `Area` belongs to a resizable `Window`
and `false` for all other `Area`s. The hit-testing region is only
expanded when this property is `true`.

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
  • Loading branch information
fleabitdev and emilk authored Jan 7, 2024
1 parent 1777bb7 commit 5432f91
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 3 deletions.
17 changes: 17 additions & 0 deletions crates/egui/src/containers/area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ pub(crate) struct State {
/// If false, clicks goes straight through to what is behind us.
/// Good for tooltips etc.
pub interactable: bool,

/// When `true`, this `Area` belongs to a resizable window, so it needs to
/// receive mouse input which occurs a short distance beyond its bounding rect.
pub edges_padded_for_resize: bool,
}

impl State {
Expand Down Expand Up @@ -71,6 +75,7 @@ pub struct Area {
pivot: Align2,
anchor: Option<(Align2, Vec2)>,
new_pos: Option<Pos2>,
edges_padded_for_resize: bool,
}

impl Area {
Expand All @@ -87,6 +92,7 @@ impl Area {
new_pos: None,
pivot: Align2::LEFT_TOP,
anchor: None,
edges_padded_for_resize: false,
}
}

Expand Down Expand Up @@ -217,6 +223,14 @@ impl Area {
Align2::LEFT_TOP
}
}

/// When `true`, this `Area` belongs to a resizable window, so it needs to
/// receive mouse input which occurs a short distance beyond its bounding rect.
#[inline]
pub(crate) fn edges_padded_for_resize(mut self, edges_padded_for_resize: bool) -> Self {
self.edges_padded_for_resize = edges_padded_for_resize;
self
}
}

pub(crate) struct Prepared {
Expand Down Expand Up @@ -261,6 +275,7 @@ impl Area {
anchor,
constrain,
constrain_rect,
edges_padded_for_resize,
} = self;

let layer_id = LayerId::new(order, id);
Expand All @@ -281,9 +296,11 @@ impl Area {
pivot,
size: Vec2::ZERO,
interactable,
edges_padded_for_resize,
});
state.pivot_pos = new_pos.unwrap_or(state.pivot_pos);
state.interactable = interactable;
state.edges_padded_for_resize = edges_padded_for_resize;

if let Some((anchor, offset)) = anchor {
let screen = ctx.available_rect();
Expand Down
10 changes: 9 additions & 1 deletion crates/egui/src/containers/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ impl<'open> Window<'open> {
/// If you need a changing title, you must call `window.id(…)` with a fixed id.
pub fn new(title: impl Into<WidgetText>) -> Self {
let title = title.into().fallback_text_style(TextStyle::Heading);
let area = Area::new(Id::new(title.text())).constrain(true);
let area = Area::new(Id::new(title.text()))
.constrain(true)
.edges_padded_for_resize(true);
Self {
title,
open: None,
Expand Down Expand Up @@ -117,6 +119,9 @@ impl<'open> Window<'open> {
#[inline]
pub fn resize(mut self, mutate: impl Fn(Resize) -> Resize) -> Self {
self.resize = mutate(self.resize);
self.area = self
.area
.edges_padded_for_resize(self.resize.is_resizable());
self
}

Expand Down Expand Up @@ -273,6 +278,7 @@ impl<'open> Window<'open> {
#[inline]
pub fn fixed_size(mut self, size: impl Into<Vec2>) -> Self {
self.resize = self.resize.fixed_size(size);
self.area = self.area.edges_padded_for_resize(false);
self
}

Expand All @@ -294,6 +300,7 @@ impl<'open> Window<'open> {
#[inline]
pub fn resizable(mut self, resizable: bool) -> Self {
self.resize = self.resize.resizable(resizable);
self.area = self.area.edges_padded_for_resize(resizable);
self
}

Expand All @@ -319,6 +326,7 @@ impl<'open> Window<'open> {
pub fn auto_sized(mut self) -> Self {
self.resize = self.resize.auto_sized();
self.scroll = ScrollArea::neither();
self.area = self.area.edges_padded_for_resize(false);
self
}

Expand Down
1 change: 1 addition & 0 deletions crates/egui/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,7 @@ impl ContextImpl {
pivot: Align2::LEFT_TOP,
size: screen_rect.size(),
interactable: true,
edges_padded_for_resize: false,
},
);

Expand Down
7 changes: 5 additions & 2 deletions crates/egui/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -862,8 +862,11 @@ impl Areas {
if let Some(state) = self.areas.get(&layer.id) {
let mut rect = state.rect();
if state.interactable {
// Allow us to resize by dragging just outside the window:
rect = rect.expand(resize_interact_radius_side);
if state.edges_padded_for_resize {
// Allow us to resize by dragging just outside the window:
rect = rect.expand(resize_interact_radius_side);
}

if rect.contains(pos) {
return Some(*layer);
}
Expand Down

0 comments on commit 5432f91

Please sign in to comment.