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.
|
* Add `egui::math::Rot2`: rotation helper.
|
||||||
* `Response` now contains the `Id` of the widget it pertains to.
|
* `Response` now contains the `Id` of the widget it pertains to.
|
||||||
* `ui.allocate_response` that allocates space and checks for interactions.
|
* `ui.allocate_response` that allocates space and checks for interactions.
|
||||||
|
* Add response.interact(sense), e.g. to check for clicks on labels.
|
||||||
|
|
||||||
### Changed 🔧
|
### Changed 🔧
|
||||||
|
|
||||||
|
|
|
@ -180,11 +180,11 @@ impl Prepared {
|
||||||
};
|
};
|
||||||
|
|
||||||
let move_response = ctx.interact(
|
let move_response = ctx.interact(
|
||||||
layer_id,
|
|
||||||
Rect::everything(),
|
Rect::everything(),
|
||||||
ctx.style().spacing.item_spacing,
|
ctx.style().spacing.item_spacing,
|
||||||
state.rect(),
|
layer_id,
|
||||||
interact_id,
|
interact_id,
|
||||||
|
state.rect(),
|
||||||
sense,
|
sense,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -198,7 +198,7 @@ impl Prepared {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check that nothing else is being interacted with
|
// 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;
|
state.offset.y -= ui.input().scroll_delta.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ struct Options {
|
||||||
pub(crate) struct FrameState {
|
pub(crate) struct FrameState {
|
||||||
/// Starts off as the screen_rect, shrinks as panels are added.
|
/// Starts off as the screen_rect, shrinks as panels are added.
|
||||||
/// The `CentralPanel` does not change this.
|
/// 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,
|
available_rect: Rect,
|
||||||
|
|
||||||
/// Starts off as the screen_rect, shrinks as panels are added.
|
/// 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) {
|
pub(crate) fn allocate_central_panel(&mut self, panel_rect: Rect) {
|
||||||
// Note: we do not shrink `available_rect`, because
|
// 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.unused_rect = Rect::nothing(); // Nothing left unused after this
|
||||||
self.used_by_panels = self.used_by_panels.union(panel_rect);
|
self.used_by_panels = self.used_by_panels.union(panel_rect);
|
||||||
}
|
}
|
||||||
|
@ -196,15 +196,27 @@ impl CtxRef {
|
||||||
/// Use `ui.interact` instead
|
/// Use `ui.interact` instead
|
||||||
pub(crate) fn interact(
|
pub(crate) fn interact(
|
||||||
&self,
|
&self,
|
||||||
layer_id: LayerId,
|
|
||||||
clip_rect: Rect,
|
clip_rect: Rect,
|
||||||
item_spacing: Vec2,
|
item_spacing: Vec2,
|
||||||
rect: Rect,
|
layer_id: LayerId,
|
||||||
id: Id,
|
id: Id,
|
||||||
|
rect: Rect,
|
||||||
sense: Sense,
|
sense: Sense,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
let interact_rect = rect.expand2((0.5 * item_spacing).min(Vec2::splat(5.0))); // make it easier to click
|
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);
|
let has_kb_focus = self.memory().has_kb_focus(id);
|
||||||
|
|
||||||
// If the the focus is lost after the call to interact,
|
// If the the focus is lost after the call to interact,
|
||||||
|
@ -215,6 +227,7 @@ impl CtxRef {
|
||||||
// Not interested or allowed input:
|
// Not interested or allowed input:
|
||||||
return Response {
|
return Response {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
|
layer_id,
|
||||||
id,
|
id,
|
||||||
rect,
|
rect,
|
||||||
sense,
|
sense,
|
||||||
|
@ -241,6 +254,7 @@ impl CtxRef {
|
||||||
if hovered {
|
if hovered {
|
||||||
let mut response = Response {
|
let mut response = Response {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
|
layer_id,
|
||||||
id,
|
id,
|
||||||
rect,
|
rect,
|
||||||
sense,
|
sense,
|
||||||
|
@ -273,6 +287,7 @@ impl CtxRef {
|
||||||
// miss
|
// miss
|
||||||
Response {
|
Response {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
|
layer_id,
|
||||||
id,
|
id,
|
||||||
rect,
|
rect,
|
||||||
sense,
|
sense,
|
||||||
|
@ -288,6 +303,7 @@ impl CtxRef {
|
||||||
let clicked = hovered && active && self.input.mouse.could_be_click;
|
let clicked = hovered && active && self.input.mouse.could_be_click;
|
||||||
Response {
|
Response {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
|
layer_id,
|
||||||
id,
|
id,
|
||||||
rect,
|
rect,
|
||||||
sense,
|
sense,
|
||||||
|
@ -301,6 +317,7 @@ impl CtxRef {
|
||||||
} else if self.input.mouse.down {
|
} else if self.input.mouse.down {
|
||||||
Response {
|
Response {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
|
layer_id,
|
||||||
id,
|
id,
|
||||||
rect,
|
rect,
|
||||||
sense,
|
sense,
|
||||||
|
@ -314,6 +331,7 @@ impl CtxRef {
|
||||||
} else {
|
} else {
|
||||||
Response {
|
Response {
|
||||||
ctx: self.clone(),
|
ctx: self.clone(),
|
||||||
|
layer_id,
|
||||||
id,
|
id,
|
||||||
rect,
|
rect,
|
||||||
sense,
|
sense,
|
||||||
|
@ -652,8 +670,7 @@ impl Context {
|
||||||
self.memory().layer_id_at(pos, resize_grab_radius_side)
|
self.memory().layer_id_at(pos, resize_grab_radius_side)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn contains_mouse(&self, layer_id: LayerId, clip_rect: Rect, rect: Rect) -> bool {
|
pub(crate) fn rect_contains_mouse(&self, layer_id: LayerId, rect: Rect) -> bool {
|
||||||
let rect = rect.intersect(clip_rect);
|
|
||||||
if let Some(mouse_pos) = self.input.mouse.pos {
|
if let Some(mouse_pos) = self.input.mouse.pos {
|
||||||
rect.contains(mouse_pos) && self.layer_id_at(mouse_pos) == Some(layer_id)
|
rect.contains(mouse_pos) && self.layer_id_at(mouse_pos) == Some(layer_id)
|
||||||
} else {
|
} 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)]
|
#[derive(Clone)]
|
||||||
pub struct Response {
|
pub struct Response {
|
||||||
// CONTEXT:
|
// CONTEXT:
|
||||||
/// Used for optionally showing a tooltip
|
/// Used for optionally showing a tooltip and checking for more interactions.
|
||||||
pub ctx: CtxRef,
|
pub ctx: CtxRef,
|
||||||
|
|
||||||
// IN:
|
// IN:
|
||||||
|
/// Which layer the widget is part of.
|
||||||
|
pub layer_id: LayerId,
|
||||||
|
|
||||||
/// The `Id` of the widget/area this response pertains.
|
/// The `Id` of the widget/area this response pertains.
|
||||||
pub id: Id,
|
pub id: Id,
|
||||||
|
|
||||||
/// The area of the screen we are talking about
|
/// The area of the screen we are talking about.
|
||||||
pub rect: Rect,
|
pub rect: Rect,
|
||||||
|
|
||||||
/// The senses (click or drag) that the widget is interested in (if any).
|
/// The senses (click or drag) that the widget is interested in (if any).
|
||||||
pub sense: Sense,
|
pub sense: Sense,
|
||||||
|
|
||||||
// OUT:
|
// OUT:
|
||||||
/// The mouse is hovering above this
|
/// The mouse is hovering above this.
|
||||||
pub hovered: bool,
|
pub hovered: bool,
|
||||||
|
|
||||||
/// The mouse clicked this thing this frame
|
/// The mouse clicked this thing this frame.
|
||||||
pub clicked: bool,
|
pub clicked: bool,
|
||||||
|
|
||||||
/// The thing was double-clicked
|
/// The thing was double-clicked.
|
||||||
pub double_clicked: bool,
|
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,
|
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,
|
pub has_kb_focus: bool,
|
||||||
|
|
||||||
/// The widget had keyboard focus and lost it,
|
/// 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 {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let Self {
|
let Self {
|
||||||
ctx: _,
|
ctx: _,
|
||||||
|
layer_id,
|
||||||
id,
|
id,
|
||||||
rect,
|
rect,
|
||||||
sense,
|
sense,
|
||||||
|
@ -105,6 +109,7 @@ impl std::fmt::Debug for Response {
|
||||||
lost_kb_focus,
|
lost_kb_focus,
|
||||||
} = self;
|
} = self;
|
||||||
f.debug_struct("Response")
|
f.debug_struct("Response")
|
||||||
|
.field("layer_id", layer_id)
|
||||||
.field("id", id)
|
.field("id", id)
|
||||||
.field("rect", rect)
|
.field("rect", rect)
|
||||||
.field("sense", sense)
|
.field("sense", sense)
|
||||||
|
@ -140,6 +145,20 @@ impl Response {
|
||||||
pub fn tooltip_text(self, text: impl Into<String>) -> Self {
|
pub fn tooltip_text(self, text: impl Into<String>) -> Self {
|
||||||
self.on_hover_text(text)
|
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 {
|
impl Response {
|
||||||
|
@ -147,8 +166,13 @@ impl Response {
|
||||||
/// For instance `a.union(b).hovered` means "was either a or b hovered?".
|
/// For instance `a.union(b).hovered` means "was either a or b hovered?".
|
||||||
pub fn union(&self, other: Self) -> Self {
|
pub fn union(&self, other: Self) -> Self {
|
||||||
assert!(self.ctx == other.ctx);
|
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 {
|
Self {
|
||||||
ctx: other.ctx,
|
ctx: other.ctx,
|
||||||
|
layer_id: self.layer_id,
|
||||||
id: self.id,
|
id: self.id,
|
||||||
rect: self.rect.union(other.rect),
|
rect: self.rect.union(other.rect),
|
||||||
sense: self.sense.union(other.sense),
|
sense: self.sense.union(other.sense),
|
||||||
|
|
|
@ -382,30 +382,40 @@ impl Ui {
|
||||||
impl Ui {
|
impl Ui {
|
||||||
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
|
pub fn interact(&self, rect: Rect, id: Id, sense: Sense) -> Response {
|
||||||
self.ctx().interact(
|
self.ctx().interact(
|
||||||
self.layer_id(),
|
|
||||||
self.clip_rect(),
|
self.clip_rect(),
|
||||||
self.style().spacing.item_spacing,
|
self.style().spacing.item_spacing,
|
||||||
rect,
|
self.layer_id(),
|
||||||
id,
|
id,
|
||||||
|
rect,
|
||||||
sense,
|
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())"]
|
#[deprecated = "Use: interact(rect, id, Sense::hover())"]
|
||||||
pub fn interact_hover(&self, rect: Rect) -> Response {
|
pub fn interact_hover(&self, rect: Rect) -> Response {
|
||||||
self.interact(rect, self.auto_id_with("hover_rect"), Sense::hover())
|
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 {
|
pub fn hovered(&self, rect: Rect) -> bool {
|
||||||
self.interact(rect, self.id, Sense::hover()).hovered
|
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!
|
// Stuff that moves the cursor, i.e. allocates space in this ui!
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue