Have non-resizable Areas ignore mouse input just beyond their bounds (#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>
This commit is contained in:
parent
1777bb7789
commit
5432f91a4d
|
|
@ -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 {
|
||||
|
|
@ -71,6 +75,7 @@ pub struct Area {
|
|||
pivot: Align2,
|
||||
anchor: Option<(Align2, Vec2)>,
|
||||
new_pos: Option<Pos2>,
|
||||
edges_padded_for_resize: bool,
|
||||
}
|
||||
|
||||
impl Area {
|
||||
|
|
@ -87,6 +92,7 @@ impl Area {
|
|||
new_pos: None,
|
||||
pivot: Align2::LEFT_TOP,
|
||||
anchor: None,
|
||||
edges_padded_for_resize: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
@ -261,6 +275,7 @@ impl Area {
|
|||
anchor,
|
||||
constrain,
|
||||
constrain_rect,
|
||||
edges_padded_for_resize,
|
||||
} = self;
|
||||
|
||||
let layer_id = LayerId::new(order, id);
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -317,6 +317,7 @@ impl ContextImpl {
|
|||
pivot: Align2::LEFT_TOP,
|
||||
size: screen_rect.size(),
|
||||
interactable: true,
|
||||
edges_padded_for_resize: false,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue