summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--graphics/src/geometry/frame.rs24
-rw-r--r--renderer/src/fallback.rs13
-rw-r--r--tiny_skia/src/geometry.rs9
-rw-r--r--wgpu/src/geometry.rs38
-rw-r--r--widget/src/mouse_area.rs49
5 files changed, 119 insertions, 14 deletions
diff --git a/graphics/src/geometry/frame.rs b/graphics/src/geometry/frame.rs
index b5f2f139..3dee7e75 100644
--- a/graphics/src/geometry/frame.rs
+++ b/graphics/src/geometry/frame.rs
@@ -65,6 +65,17 @@ where
self.raw.stroke(path, stroke);
}
+ /// Draws the stroke of an axis-aligned rectangle with the provided style
+ /// given its top-left corner coordinate and its `Size` on the [`Frame`] .
+ pub fn stroke_rectangle<'a>(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ stroke: impl Into<Stroke<'a>>,
+ ) {
+ self.raw.stroke_rectangle(top_left, size, stroke);
+ }
+
/// Draws the characters of the given [`Text`] on the [`Frame`], filling
/// them with the given color.
///
@@ -200,6 +211,12 @@ pub trait Backend: Sized {
fn paste(&mut self, frame: Self);
fn stroke<'a>(&mut self, path: &Path, stroke: impl Into<Stroke<'a>>);
+ fn stroke_rectangle<'a>(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ stroke: impl Into<Stroke<'a>>,
+ );
fn fill(&mut self, path: &Path, fill: impl Into<Fill>);
fn fill_text(&mut self, text: impl Into<Text>);
@@ -248,6 +265,13 @@ impl Backend for () {
fn paste(&mut self, _frame: Self) {}
fn stroke<'a>(&mut self, _path: &Path, _stroke: impl Into<Stroke<'a>>) {}
+ fn stroke_rectangle<'a>(
+ &mut self,
+ _top_left: Point,
+ _size: Size,
+ _stroke: impl Into<Stroke<'a>>,
+ ) {
+ }
fn fill(&mut self, _path: &Path, _fill: impl Into<Fill>) {}
fn fill_text(&mut self, _text: impl Into<Text>) {}
diff --git a/renderer/src/fallback.rs b/renderer/src/fallback.rs
index fbd285db..8cb18bde 100644
--- a/renderer/src/fallback.rs
+++ b/renderer/src/fallback.rs
@@ -540,6 +540,19 @@ mod geometry {
delegate!(self, frame, frame.stroke(path, stroke));
}
+ fn stroke_rectangle<'a>(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ stroke: impl Into<Stroke<'a>>,
+ ) {
+ delegate!(
+ self,
+ frame,
+ frame.stroke_rectangle(top_left, size, stroke)
+ );
+ }
+
fn fill_text(&mut self, text: impl Into<Text>) {
delegate!(self, frame, frame.fill_text(text));
}
diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs
index 659612d1..0d5fff62 100644
--- a/tiny_skia/src/geometry.rs
+++ b/tiny_skia/src/geometry.rs
@@ -168,6 +168,15 @@ impl geometry::frame::Backend for Frame {
});
}
+ fn stroke_rectangle<'a>(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ stroke: impl Into<Stroke<'a>>,
+ ) {
+ self.stroke(&Path::rectangle(top_left, size), stroke);
+ }
+
fn fill_text(&mut self, text: impl Into<geometry::Text>) {
let text = text.into();
diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs
index be65ba36..8e6f77d7 100644
--- a/wgpu/src/geometry.rs
+++ b/wgpu/src/geometry.rs
@@ -253,6 +253,44 @@ impl geometry::frame::Backend for Frame {
.expect("Stroke path");
}
+ fn stroke_rectangle<'a>(
+ &mut self,
+ top_left: Point,
+ size: Size,
+ stroke: impl Into<Stroke<'a>>,
+ ) {
+ let stroke = stroke.into();
+
+ let mut buffer = self
+ .buffers
+ .get_stroke(&self.transforms.current.transform_style(stroke.style));
+
+ let top_left = self
+ .transforms
+ .current
+ .0
+ .transform_point(lyon::math::Point::new(top_left.x, top_left.y));
+
+ let size =
+ self.transforms.current.0.transform_vector(
+ lyon::math::Vector::new(size.width, size.height),
+ );
+
+ let mut options = tessellation::StrokeOptions::default();
+ options.line_width = stroke.width;
+ options.start_cap = into_line_cap(stroke.line_cap);
+ options.end_cap = into_line_cap(stroke.line_cap);
+ options.line_join = into_line_join(stroke.line_join);
+
+ self.stroke_tessellator
+ .tessellate_rectangle(
+ &lyon::math::Box2D::new(top_left, top_left + size),
+ &options,
+ buffer.as_mut(),
+ )
+ .expect("Stroke rectangle");
+ }
+
fn fill_text(&mut self, text: impl Into<geometry::Text>) {
let text = text.into();
diff --git a/widget/src/mouse_area.rs b/widget/src/mouse_area.rs
index 366335f4..d255ac99 100644
--- a/widget/src/mouse_area.rs
+++ b/widget/src/mouse_area.rs
@@ -1,7 +1,4 @@
//! A container for capturing mouse events.
-
-use iced_renderer::core::Point;
-
use crate::core::event::{self, Event};
use crate::core::layout;
use crate::core::mouse;
@@ -10,7 +7,8 @@ use crate::core::renderer;
use crate::core::touch;
use crate::core::widget::{tree, Operation, Tree};
use crate::core::{
- Clipboard, Element, Layout, Length, Rectangle, Shell, Size, Vector, Widget,
+ Clipboard, Element, Layout, Length, Point, Rectangle, Shell, Size, Vector,
+ Widget,
};
/// Emit messages on mouse events.
@@ -28,8 +26,9 @@ pub struct MouseArea<
on_right_release: Option<Message>,
on_middle_press: Option<Message>,
on_middle_release: Option<Message>,
+ on_scroll: Option<Box<dyn Fn(mouse::ScrollDelta) -> Message + 'a>>,
on_enter: Option<Message>,
- on_move: Option<Box<dyn Fn(Point) -> Message>>,
+ on_move: Option<Box<dyn Fn(Point) -> Message + 'a>>,
on_exit: Option<Message>,
interaction: Option<mouse::Interaction>,
}
@@ -77,6 +76,16 @@ impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> {
self
}
+ /// The message to emit when scroll wheel is used
+ #[must_use]
+ pub fn on_scroll(
+ mut self,
+ on_scroll: impl Fn(mouse::ScrollDelta) -> Message + 'a,
+ ) -> Self {
+ self.on_scroll = Some(Box::new(on_scroll));
+ self
+ }
+
/// The message to emit when the mouse enters the area.
#[must_use]
pub fn on_enter(mut self, message: Message) -> Self {
@@ -86,11 +95,8 @@ impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> {
/// The message to emit when the mouse moves in the area.
#[must_use]
- pub fn on_move<F>(mut self, build_message: F) -> Self
- where
- F: Fn(Point) -> Message + 'static,
- {
- self.on_move = Some(Box::new(build_message));
+ pub fn on_move(mut self, on_move: impl Fn(Point) -> Message + 'a) -> Self {
+ self.on_move = Some(Box::new(on_move));
self
}
@@ -113,6 +119,8 @@ impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> {
#[derive(Default)]
struct State {
is_hovered: bool,
+ bounds: Rectangle,
+ cursor_position: Option<Point>,
}
impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> {
@@ -128,6 +136,7 @@ impl<'a, Message, Theme, Renderer> MouseArea<'a, Message, Theme, Renderer> {
on_right_release: None,
on_middle_press: None,
on_middle_release: None,
+ on_scroll: None,
on_enter: None,
on_move: None,
on_exit: None,
@@ -302,13 +311,17 @@ fn update<Message: Clone, Theme, Renderer>(
cursor: mouse::Cursor,
shell: &mut Shell<'_, Message>,
) -> event::Status {
- if let Event::Mouse(mouse::Event::CursorMoved { .. })
- | Event::Touch(touch::Event::FingerMoved { .. }) = event
- {
- let state: &mut State = tree.state.downcast_mut();
+ let state: &mut State = tree.state.downcast_mut();
+ let cursor_position = cursor.position();
+ let bounds = layout.bounds();
+
+ if state.cursor_position != cursor_position && state.bounds != bounds {
let was_hovered = state.is_hovered;
+
state.is_hovered = cursor.is_over(layout.bounds());
+ state.cursor_position = cursor_position;
+ state.bounds = bounds;
match (
widget.on_enter.as_ref(),
@@ -397,5 +410,13 @@ fn update<Message: Clone, Theme, Renderer>(
}
}
+ if let Some(on_scroll) = widget.on_scroll.as_ref() {
+ if let Event::Mouse(mouse::Event::WheelScrolled { delta }) = event {
+ shell.publish(on_scroll(delta));
+
+ return event::Status::Captured;
+ }
+ }
+
event::Status::Ignored
}