use crate::{
emath::{Align, Pos2, Rect, Vec2},
menu, Context, CursorIcon, Id, LayerId, PointerButton, Sense, Ui, WidgetText,
NUM_POINTER_BUTTONS,
};
#[derive(Clone)]
pub struct Response {
pub ctx: Context,
pub layer_id: LayerId,
pub id: Id,
pub rect: Rect,
pub sense: Sense,
#[doc(hidden)]
pub enabled: bool,
#[doc(hidden)]
pub hovered: bool,
#[doc(hidden)]
pub clicked: [bool; NUM_POINTER_BUTTONS],
#[doc(hidden)]
pub double_clicked: [bool; NUM_POINTER_BUTTONS],
pub(crate) triple_clicked: [bool; NUM_POINTER_BUTTONS],
#[doc(hidden)]
pub dragged: bool,
#[doc(hidden)]
pub drag_released: bool,
#[doc(hidden)]
pub is_pointer_button_down_on: bool,
#[doc(hidden)]
pub interact_pointer_pos: Option<Pos2>,
#[doc(hidden)]
pub changed: bool,
}
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,
enabled,
hovered,
clicked,
double_clicked,
triple_clicked,
dragged,
drag_released,
is_pointer_button_down_on,
interact_pointer_pos,
changed,
} = self;
f.debug_struct("Response")
.field("layer_id", layer_id)
.field("id", id)
.field("rect", rect)
.field("sense", sense)
.field("enabled", enabled)
.field("hovered", hovered)
.field("clicked", clicked)
.field("double_clicked", double_clicked)
.field("triple_clicked", triple_clicked)
.field("dragged", dragged)
.field("drag_released", drag_released)
.field("is_pointer_button_down_on", is_pointer_button_down_on)
.field("interact_pointer_pos", interact_pointer_pos)
.field("changed", changed)
.finish()
}
}
impl Response {
#[inline(always)]
pub fn clicked(&self) -> bool {
self.clicked[PointerButton::Primary as usize]
}
pub fn clicked_by(&self, button: PointerButton) -> bool {
self.clicked[button as usize]
}
pub fn secondary_clicked(&self) -> bool {
self.clicked[PointerButton::Secondary as usize]
}
pub fn middle_clicked(&self) -> bool {
self.clicked[PointerButton::Middle as usize]
}
pub fn double_clicked(&self) -> bool {
self.double_clicked[PointerButton::Primary as usize]
}
pub fn triple_clicked(&self) -> bool {
self.triple_clicked[PointerButton::Primary as usize]
}
pub fn double_clicked_by(&self, button: PointerButton) -> bool {
self.double_clicked[button as usize]
}
pub fn triple_clicked_by(&self, button: PointerButton) -> bool {
self.triple_clicked[button as usize]
}
pub fn clicked_elsewhere(&self) -> bool {
let pointer = &self.ctx.input().pointer;
if pointer.any_click() {
if self.hovered() {
false
} else if let Some(pos) = pointer.interact_pos() {
!self.rect.contains(pos)
} else {
false }
} else {
false
}
}
#[inline(always)]
pub fn enabled(&self) -> bool {
self.enabled
}
#[inline(always)]
pub fn hovered(&self) -> bool {
self.hovered
}
pub fn has_focus(&self) -> bool {
self.ctx.memory().has_focus(self.id)
}
pub fn gained_focus(&self) -> bool {
self.ctx.memory().gained_focus(self.id)
}
pub fn lost_focus(&self) -> bool {
self.ctx.memory().lost_focus(self.id)
}
pub fn request_focus(&self) {
self.ctx.memory().request_focus(self.id);
}
pub fn surrender_focus(&self) {
self.ctx.memory().surrender_focus(self.id);
}
#[inline(always)]
pub fn dragged(&self) -> bool {
self.dragged
}
pub fn dragged_by(&self, button: PointerButton) -> bool {
self.dragged() && self.ctx.input().pointer.button_down(button)
}
pub fn drag_started(&self) -> bool {
self.dragged && self.ctx.input().pointer.any_pressed()
}
pub fn drag_released(&self) -> bool {
self.drag_released
}
pub fn drag_delta(&self) -> Vec2 {
if self.dragged() {
self.ctx.input().pointer.delta()
} else {
Vec2::ZERO
}
}
pub fn interact_pointer_pos(&self) -> Option<Pos2> {
self.interact_pointer_pos
}
pub fn hover_pos(&self) -> Option<Pos2> {
if self.hovered() {
self.ctx.input().pointer.hover_pos()
} else {
None
}
}
#[inline(always)]
pub fn is_pointer_button_down_on(&self) -> bool {
self.is_pointer_button_down_on
}
#[inline(always)]
pub fn changed(&self) -> bool {
self.changed
}
#[inline(always)]
pub fn mark_changed(&mut self) {
self.changed = true;
}
#[doc(alias = "tooltip")]
pub fn on_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
if self.should_show_hover_ui() {
crate::containers::show_tooltip_for(
&self.ctx,
self.id.with("__tooltip"),
&self.rect,
add_contents,
);
}
self
}
pub fn on_disabled_hover_ui(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
if !self.enabled && self.ctx.rect_contains_pointer(self.layer_id, self.rect) {
crate::containers::show_tooltip_for(
&self.ctx,
self.id.with("__tooltip"),
&self.rect,
add_contents,
);
}
self
}
pub fn on_hover_ui_at_pointer(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
if self.should_show_hover_ui() {
crate::containers::show_tooltip_at_pointer(
&self.ctx,
self.id.with("__tooltip"),
add_contents,
);
}
self
}
fn should_show_hover_ui(&self) -> bool {
if self.ctx.memory().everything_is_visible() {
return true;
}
if !self.hovered || !self.ctx.input().pointer.has_pointer() {
return false;
}
if self.ctx.style().interaction.show_tooltips_only_when_still
&& !self.ctx.input().pointer.is_still()
{
self.ctx.request_repaint();
return false;
}
if self.ctx.input().pointer.any_down()
&& self.ctx.input().pointer.has_moved_too_much_for_a_click
{
return false;
}
true
}
#[doc(alias = "tooltip")]
pub fn on_hover_text_at_pointer(self, text: impl Into<WidgetText>) -> Self {
self.on_hover_ui_at_pointer(|ui| {
ui.add(crate::widgets::Label::new(text));
})
}
#[doc(alias = "tooltip")]
pub fn on_hover_text(self, text: impl Into<WidgetText>) -> Self {
self.on_hover_ui(|ui| {
ui.add(crate::widgets::Label::new(text));
})
}
pub fn on_disabled_hover_text(self, text: impl Into<WidgetText>) -> Self {
self.on_disabled_hover_ui(|ui| {
ui.add(crate::widgets::Label::new(text));
})
}
pub fn on_hover_cursor(self, cursor: CursorIcon) -> Self {
if self.hovered() {
self.ctx.output().cursor_icon = cursor;
}
self
}
pub fn interact(&self, sense: Sense) -> Self {
self.ctx.interact_with_hovered(
self.layer_id,
self.id,
self.rect,
sense,
self.enabled,
self.hovered,
)
}
pub fn scroll_to_me(&self, align: Option<Align>) {
self.ctx.frame_state().scroll_target[0] = Some((self.rect.x_range(), align));
self.ctx.frame_state().scroll_target[1] = Some((self.rect.y_range(), align));
}
pub fn widget_info(&self, make_info: impl Fn() -> crate::WidgetInfo) {
use crate::output::OutputEvent;
let event = if self.clicked() {
Some(OutputEvent::Clicked(make_info()))
} else if self.double_clicked() {
Some(OutputEvent::DoubleClicked(make_info()))
} else if self.triple_clicked() {
Some(OutputEvent::TripleClicked(make_info()))
} else if self.gained_focus() {
Some(OutputEvent::FocusGained(make_info()))
} else if self.changed {
Some(OutputEvent::ValueChanged(make_info()))
} else {
None
};
if let Some(event) = event {
self.ctx.output().events.push(event);
}
}
pub fn context_menu(self, add_contents: impl FnOnce(&mut Ui)) -> Self {
menu::context_menu(&self, add_contents);
self
}
}
impl Response {
pub fn union(&self, other: Self) -> Self {
assert!(self.ctx == other.ctx);
crate::egui_assert!(
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),
enabled: self.enabled || other.enabled,
hovered: self.hovered || other.hovered,
clicked: [
self.clicked[0] || other.clicked[0],
self.clicked[1] || other.clicked[1],
self.clicked[2] || other.clicked[2],
],
double_clicked: [
self.double_clicked[0] || other.double_clicked[0],
self.double_clicked[1] || other.double_clicked[1],
self.double_clicked[2] || other.double_clicked[2],
],
triple_clicked: [
self.triple_clicked[0] || other.triple_clicked[0],
self.triple_clicked[1] || other.triple_clicked[1],
self.triple_clicked[2] || other.triple_clicked[2],
],
dragged: self.dragged || other.dragged,
drag_released: self.drag_released || other.drag_released,
is_pointer_button_down_on: self.is_pointer_button_down_on
|| other.is_pointer_button_down_on,
interact_pointer_pos: self.interact_pointer_pos.or(other.interact_pointer_pos),
changed: self.changed || other.changed,
}
}
}
impl std::ops::BitOr for Response {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
self.union(rhs)
}
}
impl std::ops::BitOrAssign for Response {
fn bitor_assign(&mut self, rhs: Self) {
*self = self.union(rhs);
}
}
#[derive(Debug)]
pub struct InnerResponse<R> {
pub inner: R,
pub response: Response,
}
impl<R> InnerResponse<R> {
#[inline]
pub fn new(inner: R, response: Response) -> Self {
Self { inner, response }
}
}