Add response.interact(sense), e.g. to check for clicks on labels
This commit is contained in:
parent
de614153b5
commit
db3fdbe6d3
6 changed files with 78 additions and 26 deletions
|
@ -21,6 +21,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||
* Add `egui::math::Rot2`: rotation helper.
|
||||
* `Response` now contains the `Id` of the widget it pertains to.
|
||||
* `ui.allocate_response` that allocates space and checks for interactions.
|
||||
* Add response.interact(sense), e.g. to check for clicks on labels.
|
||||
|
||||
### Changed 🔧
|
||||
|
||||
|
|
|
@ -180,11 +180,11 @@ impl Prepared {
|
|||
};
|
||||
|
||||
let move_response = ctx.interact(
|
||||
layer_id,
|
||||
Rect::everything(),
|
||||
ctx.style().spacing.item_spacing,
|
||||
state.rect(),
|
||||
layer_id,
|
||||
interact_id,
|
||||
state.rect(),
|
||||
sense,
|
||||
);
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ impl Prepared {
|
|||
}
|
||||
|
||||
// TODO: check that nothing else is being interacted with
|
||||
if ui.contains_mouse(outer_rect) {
|
||||
if ui.rect_contains_mouse(outer_rect) {
|
||||
state.offset.y -= ui.input().scroll_delta.y;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ struct Options {
|
|||
pub(crate) struct FrameState {
|
||||
/// Starts off as the screen_rect, shrinks as panels are added.
|
||||
/// The `CentralPanel` does not change this.
|
||||
/// This is the area avilable to Window's.
|
||||
/// This is the area available to Window's.
|
||||
available_rect: Rect,
|
||||
|
||||
/// Starts off as the screen_rect, shrinks as panels are added.
|
||||
|
@ -93,7 +93,7 @@ impl FrameState {
|
|||
|
||||
pub(crate) fn allocate_central_panel(&mut self, panel_rect: Rect) {
|
||||
// Note: we do not shrink `available_rect`, because
|
||||
// we alllow windows to cover the CentralPanel.
|
||||
// we allow windows to cover the CentralPanel.
|
||||
self.unused_rect = Rect::nothing(); // Nothing left unused after this
|
||||
self.used_by_panels = self.used_by_panels.union(panel_rect);
|
||||
}
|
||||
|
@ -196,15 +196,27 @@ impl CtxRef {
|
|||
/// Use `ui.interact` instead
|
||||
pub(crate) fn interact(
|
||||
&self,
|
||||
layer_id: LayerId,
|
||||
clip_rect: Rect,
|
||||
item_spacing: Vec2,
|
||||
rect: Rect,
|
||||
layer_id: LayerId,
|
||||
id: Id,
|
||||
rect: Rect,
|
||||
sense: Sense,
|
||||
) -> Response {
|
||||
let interact_rect = rect.expand2((0.5 * item_spacing).min(Vec2::splat(5.0))); // make it easier to click
|
||||
let hovered = self.contains_mouse(layer_id, clip_rect, interact_rect);
|
||||
let hovered = self.rect_contains_mouse(layer_id, clip_rect.intersect(interact_rect));
|
||||
self.interact_with_hovered(layer_id, id, rect, sense, hovered)
|
||||
}
|
||||
|
||||
/// You specify if a thing is hovered, and the function gives a `Response`.
|
||||
pub(crate) fn interact_with_hovered(
|
||||
&self,
|
||||
layer_id: LayerId,
|
||||
id: Id,
|
||||
rect: Rect,
|
||||
sense: Sense,
|
||||
hovered: bool,
|
||||
) -> Response {
|
||||
let has_kb_focus = self.memory().has_kb_focus(id);
|
||||
|
||||
// If the the focus is lost after the call to interact,
|
||||
|
@ -215,6 +227,7 @@ impl CtxRef {
|
|||
// Not interested or allowed input:
|
||||
return Response {
|
||||
ctx: self.clone(),
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
|
@ -241,6 +254,7 @@ impl CtxRef {
|
|||
if hovered {
|
||||
let mut response = Response {
|
||||
ctx: self.clone(),
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
|
@ -273,6 +287,7 @@ impl CtxRef {
|
|||
// miss
|
||||
Response {
|
||||
ctx: self.clone(),
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
|
@ -288,6 +303,7 @@ impl CtxRef {
|
|||
let clicked = hovered && active && self.input.mouse.could_be_click;
|
||||
Response {
|
||||
ctx: self.clone(),
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
|
@ -301,6 +317,7 @@ impl CtxRef {
|
|||
} else if self.input.mouse.down {
|
||||
Response {
|
||||
ctx: self.clone(),
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
|
@ -314,6 +331,7 @@ impl CtxRef {
|
|||
} else {
|
||||
Response {
|
||||
ctx: self.clone(),
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
|
@ -652,8 +670,7 @@ impl Context {
|
|||
self.memory().layer_id_at(pos, resize_grab_radius_side)
|
||||
}
|
||||
|
||||
pub fn contains_mouse(&self, layer_id: LayerId, clip_rect: Rect, rect: Rect) -> bool {
|
||||
let rect = rect.intersect(clip_rect);
|
||||
pub(crate) fn rect_contains_mouse(&self, layer_id: LayerId, rect: Rect) -> bool {
|
||||
if let Some(mouse_pos) = self.input.mouse.pos {
|
||||
rect.contains(mouse_pos) && self.layer_id_at(mouse_pos) == Some(layer_id)
|
||||
} else {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{math::Rect, CtxRef, Id, Ui};
|
||||
use crate::{math::Rect, CtxRef, Id, LayerId, Ui};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
|
@ -54,33 +54,36 @@ impl Default for CursorIcon {
|
|||
#[derive(Clone)]
|
||||
pub struct Response {
|
||||
// CONTEXT:
|
||||
/// Used for optionally showing a tooltip
|
||||
/// Used for optionally showing a tooltip and checking for more interactions.
|
||||
pub ctx: CtxRef,
|
||||
|
||||
// IN:
|
||||
/// Which layer the widget is part of.
|
||||
pub layer_id: LayerId,
|
||||
|
||||
/// The `Id` of the widget/area this response pertains.
|
||||
pub id: Id,
|
||||
|
||||
/// The area of the screen we are talking about
|
||||
/// The area of the screen we are talking about.
|
||||
pub rect: Rect,
|
||||
|
||||
/// The senses (click or drag) that the widget is interested in (if any).
|
||||
pub sense: Sense,
|
||||
|
||||
// OUT:
|
||||
/// The mouse is hovering above this
|
||||
/// The mouse is hovering above this.
|
||||
pub hovered: bool,
|
||||
|
||||
/// The mouse clicked this thing this frame
|
||||
/// The mouse clicked this thing this frame.
|
||||
pub clicked: bool,
|
||||
|
||||
/// The thing was double-clicked
|
||||
/// The thing was double-clicked.
|
||||
pub double_clicked: bool,
|
||||
|
||||
/// The mouse is interacting with this thing (e.g. dragging it)
|
||||
/// The mouse is interacting with this thing (e.g. dragging it).
|
||||
pub active: bool,
|
||||
|
||||
/// This widget has the keyboard focus (i.e. is receiving key pressed)
|
||||
/// This widget has the keyboard focus (i.e. is receiving key pressed).
|
||||
pub has_kb_focus: bool,
|
||||
|
||||
/// The widget had keyboard focus and lost it,
|
||||
|
@ -94,6 +97,7 @@ impl std::fmt::Debug for Response {
|
|||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let Self {
|
||||
ctx: _,
|
||||
layer_id,
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
|
@ -105,6 +109,7 @@ impl std::fmt::Debug for Response {
|
|||
lost_kb_focus,
|
||||
} = self;
|
||||
f.debug_struct("Response")
|
||||
.field("layer_id", layer_id)
|
||||
.field("id", id)
|
||||
.field("rect", rect)
|
||||
.field("sense", sense)
|
||||
|
@ -140,6 +145,20 @@ impl Response {
|
|||
pub fn tooltip_text(self, text: impl Into<String>) -> Self {
|
||||
self.on_hover_text(text)
|
||||
}
|
||||
|
||||
/// Check for more interactions (e.g. sense clicks on a `Response` returned from a label).
|
||||
///
|
||||
/// ```
|
||||
/// # let mut ui = egui::Ui::__test();
|
||||
/// let response = ui.label("hello");
|
||||
/// assert!(!response.clicked); // labels don't sense clicks
|
||||
/// let response = response.interact(egui::Sense::click());
|
||||
/// if response.clicked { /* … */ }
|
||||
/// ```
|
||||
pub fn interact(&self, sense: Sense) -> Self {
|
||||
self.ctx
|
||||
.interact_with_hovered(self.layer_id, self.id, self.rect, sense, self.hovered)
|
||||
}
|
||||
}
|
||||
|
||||
impl Response {
|
||||
|
@ -147,8 +166,13 @@ impl Response {
|
|||
/// For instance `a.union(b).hovered` means "was either a or b hovered?".
|
||||
pub fn union(&self, other: Self) -> Self {
|
||||
assert!(self.ctx == other.ctx);
|
||||
debug_assert_eq!(
|
||||
self.layer_id, other.layer_id,
|
||||
"It makes no sense to combine Responses from two different layers"
|
||||
);
|
||||
Self {
|
||||
ctx: other.ctx,
|
||||
layer_id: self.layer_id,
|
||||
id: self.id,
|
||||
rect: self.rect.union(other.rect),
|
||||
sense: self.sense.union(other.sense),
|
||||
|
|
|
@ -382,30 +382,40 @@ impl Ui {
|
|||
impl Ui {
|
||||
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
|
||||
self.ctx().interact(
|
||||
self.layer_id(),
|
||||
self.clip_rect(),
|
||||
self.style().spacing.item_spacing,
|
||||
rect,
|
||||
self.layer_id(),
|
||||
id,
|
||||
rect,
|
||||
sense,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn rect_contains_mouse(&self, rect: Rect) -> bool {
|
||||
self.ctx()
|
||||
.rect_contains_mouse(self.layer_id(), self.clip_rect().intersect(rect))
|
||||
}
|
||||
|
||||
/// Is the mouse above this `Ui`?
|
||||
pub fn ui_contains_mouse(&self) -> bool {
|
||||
if let Some(mouse_pos) = self.input().mouse.pos {
|
||||
self.clip_rect().contains(mouse_pos)
|
||||
&& self.ctx().layer_id_at(mouse_pos) == Some(self.layer_id())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated = "Use: interact(rect, id, Sense::hover())"]
|
||||
pub fn interact_hover(&self, rect: Rect) -> Response {
|
||||
self.interact(rect, self.auto_id_with("hover_rect"), Sense::hover())
|
||||
}
|
||||
|
||||
#[deprecated = "Use: contains_mouse()"]
|
||||
#[deprecated = "Use: ui_contains_mouse()"]
|
||||
pub fn hovered(&self, rect: Rect) -> bool {
|
||||
self.interact(rect, self.id, Sense::hover()).hovered
|
||||
}
|
||||
|
||||
pub fn contains_mouse(&self, rect: Rect) -> bool {
|
||||
self.ctx()
|
||||
.contains_mouse(self.layer_id(), self.clip_rect(), rect)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
// Stuff that moves the cursor, i.e. allocates space in this ui!
|
||||
|
||||
|
|
Loading…
Reference in a new issue