Introduce opaque widget helper

This commit is contained in:
Héctor Ramón Jiménez 2024-04-25 06:05:00 +02:00
parent 9492da11d9
commit 4cd45643d7
No known key found for this signature in database
GPG key ID: 7CC46565708259A7
13 changed files with 182 additions and 12 deletions

View file

@ -3,6 +3,7 @@
#[allow(missing_docs)]
pub enum Interaction {
#[default]
None,
Idle,
Pointer,
Grab,

View file

@ -79,7 +79,7 @@ where
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
mouse::Interaction::Idle
mouse::Interaction::None
}
/// Returns true if the cursor is over the [`Overlay`].

View file

@ -137,7 +137,7 @@ where
_viewport: &Rectangle,
_renderer: &Renderer,
) -> mouse::Interaction {
mouse::Interaction::Idle
mouse::Interaction::None
}
/// Returns the overlay of the [`Widget`], if there is any.

View file

@ -159,7 +159,7 @@ mod loupe {
if cursor.is_over(layout.bounds()) {
mouse::Interaction::ZoomIn
} else {
mouse::Interaction::Idle
mouse::Interaction::None
}
}
}

View file

@ -48,7 +48,7 @@ where
caches,
queued_events: Vec::new(),
queued_messages: Vec::new(),
mouse_interaction: mouse::Interaction::Idle,
mouse_interaction: mouse::Interaction::None,
}
}

View file

@ -47,7 +47,7 @@ where
cache,
queued_events: Vec::new(),
queued_messages: Vec::new(),
mouse_interaction: mouse::Interaction::Idle,
mouse_interaction: mouse::Interaction::None,
}
}

View file

@ -5,7 +5,7 @@ use crate::combo_box::{self, ComboBox};
use crate::container::{self, Container};
use crate::core;
use crate::core::widget::operation;
use crate::core::{Element, Length, Pixels};
use crate::core::{Element, Length, Pixels, Widget};
use crate::keyed;
use crate::overlay;
use crate::pick_list::{self, PickList};
@ -123,6 +123,173 @@ where
Stack::with_children(children)
}
/// Wraps the given widget and captures any mouse button presses inside the bounds of
/// the widget—therefore making it _opaque_.
///
/// This helper is meant to be used to mark elements in a [`Stack`] to avoid mouse
/// events from passing through layers.
///
/// [`Stack`]: crate::Stack
pub fn opaque<'a, Message, Theme, Renderer>(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Element<'a, Message, Theme, Renderer>
where
Message: 'a,
Theme: 'a,
Renderer: core::Renderer + 'a,
{
use crate::core::event::{self, Event};
use crate::core::layout::{self, Layout};
use crate::core::mouse;
use crate::core::renderer;
use crate::core::widget::tree::{self, Tree};
use crate::core::{Rectangle, Shell, Size};
struct Opaque<'a, Message, Theme, Renderer> {
content: Element<'a, Message, Theme, Renderer>,
}
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
for Opaque<'a, Message, Theme, Renderer>
where
Renderer: core::Renderer,
{
fn tag(&self) -> tree::Tag {
self.content.as_widget().tag()
}
fn state(&self) -> tree::State {
self.content.as_widget().state()
}
fn children(&self) -> Vec<Tree> {
self.content.as_widget().children()
}
fn diff(&self, tree: &mut Tree) {
self.content.as_widget().diff(tree);
}
fn size(&self) -> Size<Length> {
self.content.as_widget().size()
}
fn size_hint(&self) -> Size<Length> {
self.content.as_widget().size_hint()
}
fn layout(
&self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
self.content.as_widget().layout(tree, renderer, limits)
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
style: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
) {
self.content
.as_widget()
.draw(tree, renderer, theme, style, layout, cursor, viewport);
}
fn operate(
&self,
state: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn operation::Operation<Message>,
) {
self.content
.as_widget()
.operate(state, layout, renderer, operation);
}
fn on_event(
&mut self,
state: &mut Tree,
event: Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn core::Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) -> event::Status {
let is_mouse_press = matches!(
event,
core::Event::Mouse(mouse::Event::ButtonPressed(_))
);
if let core::event::Status::Captured =
self.content.as_widget_mut().on_event(
state, event, layout, cursor, renderer, clipboard, shell,
viewport,
)
{
return event::Status::Captured;
}
if is_mouse_press && cursor.is_over(layout.bounds()) {
event::Status::Captured
} else {
event::Status::Ignored
}
}
fn mouse_interaction(
&self,
state: &core::widget::Tree,
layout: core::Layout<'_>,
cursor: core::mouse::Cursor,
viewport: &core::Rectangle,
renderer: &Renderer,
) -> core::mouse::Interaction {
let interaction = self
.content
.as_widget()
.mouse_interaction(state, layout, cursor, viewport, renderer);
if interaction == mouse::Interaction::None
&& cursor.is_over(layout.bounds())
{
mouse::Interaction::Idle
} else {
interaction
}
}
fn overlay<'b>(
&'b mut self,
state: &'b mut core::widget::Tree,
layout: core::Layout<'_>,
renderer: &Renderer,
translation: core::Vector,
) -> Option<core::overlay::Element<'b, Message, Theme, Renderer>>
{
self.content.as_widget_mut().overlay(
state,
layout,
renderer,
translation,
)
}
}
Element::new(Opaque {
content: content.into(),
})
}
/// Creates a new [`Scrollable`] with the provided content.
///
/// [`Scrollable`]: crate::Scrollable

View file

@ -304,7 +304,7 @@ where
} else if is_mouse_over {
mouse::Interaction::Grab
} else {
mouse::Interaction::Idle
mouse::Interaction::None
}
}

View file

@ -232,7 +232,7 @@ where
);
match (self.interaction, content_interaction) {
(Some(interaction), mouse::Interaction::Idle)
(Some(interaction), mouse::Interaction::None)
if cursor.is_over(layout.bounds()) =>
{
interaction

View file

@ -857,7 +857,7 @@ where
if (mouse_over_x_scrollbar || mouse_over_y_scrollbar)
|| state.scrollers_grabbed()
{
mouse::Interaction::Idle
mouse::Interaction::None
} else {
let translation =
state.translation(self.direction, bounds, content_bounds);

View file

@ -249,7 +249,7 @@ where
state, layout, cursor, viewport, renderer,
)
})
.find(|&interaction| interaction != mouse::Interaction::Idle)
.find(|&interaction| interaction != mouse::Interaction::None)
.unwrap_or_default()
}

View file

@ -396,7 +396,9 @@ pub fn mouse_interaction(
use mouse::Interaction;
match interaction {
Interaction::Idle => winit::window::CursorIcon::Default,
Interaction::None | Interaction::Idle => {
winit::window::CursorIcon::Default
}
Interaction::Pointer => winit::window::CursorIcon::Pointer,
Interaction::Working => winit::window::CursorIcon::Progress,
Interaction::Grab => winit::window::CursorIcon::Grab,

View file

@ -60,7 +60,7 @@ where
exit_on_close_request,
surface,
renderer,
mouse_interaction: mouse::Interaction::Idle,
mouse_interaction: mouse::Interaction::None,
},
);