Add ability to highlight any widget (#2632)
* Add ability to highlight any widget * Add line to changelog * Demote the demo to a test
This commit is contained in:
parent
e7c0547e23
commit
c72bdb77b5
10 changed files with 99 additions and 4 deletions
|
@ -24,6 +24,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
|
|||
* Add `WidgetVisuals::optional_bg_color` - set it to `Color32::TRANSPARENT` to hide button backgrounds ([#2621](https://github.com/emilk/egui/pull/2621)).
|
||||
* Add `Context::screen_rect` and `Context::set_cursor_icon` ([#2625](https://github.com/emilk/egui/pull/2625)).
|
||||
* You can turn off the vertical line left of indented regions with `Visuals::indent_has_left_vline` ([#2636](https://github.com/emilk/egui/pull/2636)).
|
||||
* Add `Response.highlight` to highlight a widget ([#2632](https://github.com/emilk/egui/pull/2632)).
|
||||
|
||||
### Changed 🔧
|
||||
* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)).
|
||||
|
|
|
@ -621,6 +621,8 @@ impl Context {
|
|||
) -> Response {
|
||||
let hovered = hovered && enabled; // can't even hover disabled widgets
|
||||
|
||||
let highlighted = self.frame_state(|fs| fs.highlight_this_frame.contains(&id));
|
||||
|
||||
let mut response = Response {
|
||||
ctx: self.clone(),
|
||||
layer_id,
|
||||
|
@ -629,6 +631,7 @@ impl Context {
|
|||
sense,
|
||||
enabled,
|
||||
hovered,
|
||||
highlighted,
|
||||
clicked: Default::default(),
|
||||
double_clicked: Default::default(),
|
||||
triple_clicked: Default::default(),
|
||||
|
@ -1284,6 +1287,15 @@ impl Context {
|
|||
pub fn wants_keyboard_input(&self) -> bool {
|
||||
self.memory(|m| m.interaction.focus.focused().is_some())
|
||||
}
|
||||
|
||||
/// Highlight this widget, to make it look like it is hovered, even if it isn't.
|
||||
///
|
||||
/// The highlight takes on frame to take effect if you call this after the widget has been fully rendered.
|
||||
///
|
||||
/// See also [`Response::highlight`].
|
||||
pub fn highlight_widget(&self, id: Id) {
|
||||
self.frame_state_mut(|fs| fs.highlight_next_frame.insert(id));
|
||||
}
|
||||
}
|
||||
|
||||
// Ergonomic methods to forward some calls often used in 'if let' without holding the borrow
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use std::ops::RangeInclusive;
|
||||
|
||||
use crate::*;
|
||||
use crate::{id::IdSet, *};
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) struct TooltipFrameState {
|
||||
|
@ -51,6 +51,12 @@ pub(crate) struct FrameState {
|
|||
|
||||
#[cfg(feature = "accesskit")]
|
||||
pub(crate) accesskit_state: Option<AccessKitFrameState>,
|
||||
|
||||
/// Highlight these widgets this next frame. Read from this.
|
||||
pub(crate) highlight_this_frame: IdSet,
|
||||
|
||||
/// Highlight these widgets the next frame. Write to this.
|
||||
pub(crate) highlight_next_frame: IdSet,
|
||||
}
|
||||
|
||||
impl Default for FrameState {
|
||||
|
@ -65,6 +71,8 @@ impl Default for FrameState {
|
|||
scroll_target: [None, None],
|
||||
#[cfg(feature = "accesskit")]
|
||||
accesskit_state: None,
|
||||
highlight_this_frame: Default::default(),
|
||||
highlight_next_frame: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,6 +89,8 @@ impl FrameState {
|
|||
scroll_target,
|
||||
#[cfg(feature = "accesskit")]
|
||||
accesskit_state,
|
||||
highlight_this_frame,
|
||||
highlight_next_frame,
|
||||
} = self;
|
||||
|
||||
used_ids.clear();
|
||||
|
@ -90,10 +100,13 @@ impl FrameState {
|
|||
*tooltip_state = None;
|
||||
*scroll_delta = input.scroll_delta;
|
||||
*scroll_target = [None, None];
|
||||
|
||||
#[cfg(feature = "accesskit")]
|
||||
{
|
||||
*accesskit_state = None;
|
||||
}
|
||||
|
||||
*highlight_this_frame = std::mem::take(highlight_next_frame);
|
||||
}
|
||||
|
||||
/// How much space is still available after panels has been added.
|
||||
|
|
|
@ -168,5 +168,8 @@ impl std::hash::BuildHasher for BuilIdHasher {
|
|||
}
|
||||
}
|
||||
|
||||
/// `IdSet` is a `HashSet<Id>` optimized by knowing that [`Id`] has good entropy, and doesn't need more hashing.
|
||||
pub type IdSet = std::collections::HashSet<Id, BuilIdHasher>;
|
||||
|
||||
/// `IdMap<V>` is a `HashMap<Id, V>` optimized by knowing that [`Id`] has good entropy, and doesn't need more hashing.
|
||||
pub type IdMap<V> = std::collections::HashMap<Id, V, BuilIdHasher>;
|
||||
|
|
|
@ -13,6 +13,7 @@ use crate::{
|
|||
///
|
||||
/// Whenever something gets added to a [`Ui`], a [`Response`] object is returned.
|
||||
/// [`ui.add`] returns a [`Response`], as does [`ui.button`], and all similar shortcuts.
|
||||
// TODO(emilk): we should be using bit sets instead of so many bools
|
||||
#[derive(Clone)]
|
||||
pub struct Response {
|
||||
// CONTEXT:
|
||||
|
@ -42,6 +43,10 @@ pub struct Response {
|
|||
#[doc(hidden)]
|
||||
pub hovered: bool,
|
||||
|
||||
/// The widget is highlighted via a call to [`Self::highlight`] or [`Context::highlight_widget`].
|
||||
#[doc(hidden)]
|
||||
pub highlighted: bool,
|
||||
|
||||
/// The pointer clicked this thing this frame.
|
||||
#[doc(hidden)]
|
||||
pub clicked: [bool; NUM_POINTER_BUTTONS],
|
||||
|
@ -90,6 +95,7 @@ impl std::fmt::Debug for Response {
|
|||
sense,
|
||||
enabled,
|
||||
hovered,
|
||||
highlighted,
|
||||
clicked,
|
||||
double_clicked,
|
||||
triple_clicked,
|
||||
|
@ -106,6 +112,7 @@ impl std::fmt::Debug for Response {
|
|||
.field("sense", sense)
|
||||
.field("enabled", enabled)
|
||||
.field("hovered", hovered)
|
||||
.field("highlighted", highlighted)
|
||||
.field("clicked", clicked)
|
||||
.field("double_clicked", double_clicked)
|
||||
.field("triple_clicked", triple_clicked)
|
||||
|
@ -213,6 +220,12 @@ impl Response {
|
|||
self.hovered
|
||||
}
|
||||
|
||||
/// The widget is highlighted via a call to [`Self::highlight`] or [`Context::highlight_widget`].
|
||||
#[doc(hidden)]
|
||||
pub fn highlighted(&self) -> bool {
|
||||
self.highlighted
|
||||
}
|
||||
|
||||
/// This widget has the keyboard focus (i.e. is receiving key presses).
|
||||
///
|
||||
/// This function only returns true if the UI as a whole (e.g. window)
|
||||
|
@ -454,6 +467,17 @@ impl Response {
|
|||
})
|
||||
}
|
||||
|
||||
/// Highlight this widget, to make it look like it is hovered, even if it isn't.
|
||||
///
|
||||
/// The highlight takes on frame to take effect if you call this after the widget has been fully rendered.
|
||||
///
|
||||
/// See also [`Context::highlight_widget`].
|
||||
pub fn highlight(mut self) -> Self {
|
||||
self.ctx.highlight_widget(self.id);
|
||||
self.highlighted = true;
|
||||
self
|
||||
}
|
||||
|
||||
/// Show this text when hovering if the widget is disabled.
|
||||
pub fn on_disabled_hover_text(self, text: impl Into<WidgetText>) -> Self {
|
||||
self.on_disabled_hover_ui(|ui| {
|
||||
|
@ -688,6 +712,7 @@ impl Response {
|
|||
sense: self.sense.union(other.sense),
|
||||
enabled: self.enabled || other.enabled,
|
||||
hovered: self.hovered || other.hovered,
|
||||
highlighted: self.highlighted || other.highlighted,
|
||||
clicked: [
|
||||
self.clicked[0] || other.clicked[0],
|
||||
self.clicked[1] || other.clicked[1],
|
||||
|
|
|
@ -576,7 +576,9 @@ pub struct Widgets {
|
|||
/// The style of an interactive widget, such as a button, at rest.
|
||||
pub inactive: WidgetVisuals,
|
||||
|
||||
/// The style of an interactive widget while you hover it.
|
||||
/// The style of an interactive widget while you hover it, or when it is highlighted.
|
||||
///
|
||||
/// See [`Response::hovered`], [`Response::highlighted`] and [`Response::highlight`].
|
||||
pub hovered: WidgetVisuals,
|
||||
|
||||
/// The style of an interactive widget as you are clicking or dragging it.
|
||||
|
@ -592,7 +594,7 @@ impl Widgets {
|
|||
&self.noninteractive
|
||||
} else if response.is_pointer_button_down_on() || response.has_focus() {
|
||||
&self.active
|
||||
} else if response.hovered() {
|
||||
} else if response.hovered() | response.highlighted() {
|
||||
&self.hovered
|
||||
} else {
|
||||
&self.inactive
|
||||
|
|
|
@ -173,7 +173,7 @@ impl Widget for Label {
|
|||
if ui.is_rect_visible(response.rect) {
|
||||
let response_color = ui.style().interact(&response).text_color();
|
||||
|
||||
let underline = if response.has_focus() {
|
||||
let underline = if response.has_focus() || response.highlighted() {
|
||||
Stroke::new(1.0, response_color)
|
||||
} else {
|
||||
Stroke::NONE
|
||||
|
|
|
@ -90,6 +90,7 @@ impl Default for Tests {
|
|||
fn default() -> Self {
|
||||
Self::from_demos(vec![
|
||||
Box::new(super::tests::CursorTest::default()),
|
||||
Box::new(super::highlighting::Highlighting::default()),
|
||||
Box::new(super::tests::IdTest::default()),
|
||||
Box::new(super::tests::InputTest::default()),
|
||||
Box::new(super::layout_test::LayoutTest::default()),
|
||||
|
|
37
crates/egui_demo_lib/src/demo/highlighting.rs
Normal file
37
crates/egui_demo_lib/src/demo/highlighting.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
#[derive(Default)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(feature = "serde", serde(default))]
|
||||
pub struct Highlighting {}
|
||||
|
||||
impl super::Demo for Highlighting {
|
||||
fn name(&self) -> &'static str {
|
||||
"Highlighting"
|
||||
}
|
||||
|
||||
fn show(&mut self, ctx: &egui::Context, open: &mut bool) {
|
||||
egui::Window::new(self.name())
|
||||
.default_width(320.0)
|
||||
.open(open)
|
||||
.show(ctx, |ui| {
|
||||
use super::View as _;
|
||||
self.ui(ui);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl super::View for Highlighting {
|
||||
fn ui(&mut self, ui: &mut egui::Ui) {
|
||||
ui.label("This demo demonstrates highlighting a widget.");
|
||||
ui.add_space(4.0);
|
||||
let label_response = ui.label("Hover me to highlight the button!");
|
||||
ui.add_space(4.0);
|
||||
let mut button_response = ui.button("Hover the button to highlight the label!");
|
||||
|
||||
if label_response.hovered() {
|
||||
button_response = button_response.highlight();
|
||||
}
|
||||
if button_response.hovered() {
|
||||
label_response.highlight();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,7 @@ pub mod dancing_strings;
|
|||
pub mod demo_app_windows;
|
||||
pub mod drag_and_drop;
|
||||
pub mod font_book;
|
||||
pub mod highlighting;
|
||||
pub mod layout_test;
|
||||
pub mod misc_demo_window;
|
||||
pub mod multi_touch;
|
||||
|
|
Loading…
Reference in a new issue