diff options
-rw-r--r-- | graphics/src/geometry/frame.rs | 24 | ||||
-rw-r--r-- | renderer/src/fallback.rs | 13 | ||||
-rw-r--r-- | tiny_skia/src/geometry.rs | 9 | ||||
-rw-r--r-- | wgpu/src/geometry.rs | 38 | ||||
-rw-r--r-- | widget/src/mouse_area.rs | 49 |
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 } |