diff options
author | 2019-11-03 05:19:12 +0100 | |
---|---|---|
committer | 2019-11-03 05:19:12 +0100 | |
commit | 0ea911ae36bbde8c288f7ae1ba8a0049b696d7c4 (patch) | |
tree | 929cb493b674512520f1b6a92f86d7a09e5801f6 /native | |
parent | de3c87b9a793c0d0799948e16ad1b14e5b4892ba (diff) | |
parent | 022dc0139b7437f167a8d3ae483bf8e83f1dab04 (diff) | |
download | iced-0ea911ae36bbde8c288f7ae1ba8a0049b696d7c4.tar.gz iced-0ea911ae36bbde8c288f7ae1ba8a0049b696d7c4.tar.bz2 iced-0ea911ae36bbde8c288f7ae1ba8a0049b696d7c4.zip |
Merge pull request #35 from hecrj/feature/scrollables
Scrollable widget
Diffstat (limited to '')
-rw-r--r-- | native/Cargo.toml | 2 | ||||
-rw-r--r-- | native/src/element.rs | 13 | ||||
-rw-r--r-- | native/src/input/mouse.rs | 2 | ||||
-rw-r--r-- | native/src/input/mouse/event.rs | 17 | ||||
-rw-r--r-- | native/src/user_interface.rs | 8 | ||||
-rw-r--r-- | native/src/widget.rs | 4 | ||||
-rw-r--r-- | native/src/widget/button.rs | 1 | ||||
-rw-r--r-- | native/src/widget/checkbox.rs | 1 | ||||
-rw-r--r-- | native/src/widget/column.rs | 11 | ||||
-rw-r--r-- | native/src/widget/radio.rs | 1 | ||||
-rw-r--r-- | native/src/widget/row.rs | 11 | ||||
-rw-r--r-- | native/src/widget/scrollable.rs | 204 | ||||
-rw-r--r-- | native/src/widget/slider.rs | 1 |
13 files changed, 259 insertions, 17 deletions
diff --git a/native/Cargo.toml b/native/Cargo.toml index 8cabe94c..bb6139d6 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -11,4 +11,4 @@ repository = "https://github.com/hecrj/iced" iced_core = { version = "0.1.0-alpha", path = "../core" } stretch = "0.2" twox-hash = "1.5" -raw-window-handle = "0.1" +raw-window-handle = "0.3" diff --git a/native/src/element.rs b/native/src/element.rs index bbedd942..c638c713 100644 --- a/native/src/element.rs +++ b/native/src/element.rs @@ -299,6 +299,7 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<B>, + renderer: &Renderer, ) { let mut original_messages = Vec::new(); @@ -307,6 +308,7 @@ where layout, cursor_position, &mut original_messages, + renderer, ); original_messages @@ -369,10 +371,15 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<Message>, + renderer: &Renderer, ) { - self.element - .widget - .on_event(event, layout, cursor_position, messages) + self.element.widget.on_event( + event, + layout, + cursor_position, + messages, + renderer, + ) } fn draw( diff --git a/native/src/input/mouse.rs b/native/src/input/mouse.rs index d37f5b96..69dc6b4c 100644 --- a/native/src/input/mouse.rs +++ b/native/src/input/mouse.rs @@ -3,4 +3,4 @@ mod button; mod event; pub use button::Button; -pub use event::Event; +pub use event::{Event, ScrollDelta}; diff --git a/native/src/input/mouse/event.rs b/native/src/input/mouse/event.rs index 7b68208f..478f9b4d 100644 --- a/native/src/input/mouse/event.rs +++ b/native/src/input/mouse/event.rs @@ -34,11 +34,22 @@ pub enum Event { }, /// The mouse wheel was scrolled. - WheelScrolled { + WheelScrolled { delta: ScrollDelta }, +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum ScrollDelta { + Lines { /// The number of horizontal lines scrolled - delta_x: f32, + x: f32, /// The number of vertical lines scrolled - delta_y: f32, + y: f32, + }, + Pixels { + /// The number of horizontal pixels scrolled + x: f32, + /// The number of vertical pixels scrolled + y: f32, }, } diff --git a/native/src/user_interface.rs b/native/src/user_interface.rs index 5675076d..0760dd7e 100644 --- a/native/src/user_interface.rs +++ b/native/src/user_interface.rs @@ -186,7 +186,7 @@ where /// ); /// /// // Update the user interface - /// let messages = user_interface.update(events.drain(..)); + /// let messages = user_interface.update(&renderer, events.drain(..)); /// /// cache = user_interface.into_cache(); /// @@ -198,6 +198,7 @@ where /// ``` pub fn update( &mut self, + renderer: &Renderer, events: impl Iterator<Item = Event>, ) -> Vec<Message> { let mut messages = Vec::new(); @@ -212,6 +213,7 @@ where Layout::new(&self.layout), self.cursor_position, &mut messages, + renderer, ); } @@ -281,7 +283,7 @@ where /// &mut renderer, /// ); /// - /// let messages = user_interface.update(events.drain(..)); + /// let messages = user_interface.update(&renderer, events.drain(..)); /// /// // Draw the user interface /// let mouse_cursor = user_interface.draw(&mut renderer); @@ -347,7 +349,7 @@ impl Cache { .0 .compute_layout(geometry::Size::undefined()) .unwrap(), - cursor_position: Point::new(0.0, 0.0), + cursor_position: Point::new(-1.0, -1.0), } } } diff --git a/native/src/widget.rs b/native/src/widget.rs index bcef2665..c04f3377 100644 --- a/native/src/widget.rs +++ b/native/src/widget.rs @@ -26,6 +26,7 @@ pub mod column; pub mod image; pub mod radio; pub mod row; +pub mod scrollable; pub mod slider; pub mod text; @@ -42,6 +43,8 @@ pub use radio::Radio; #[doc(no_inline)] pub use row::Row; #[doc(no_inline)] +pub use scrollable::Scrollable; +#[doc(no_inline)] pub use slider::Slider; #[doc(no_inline)] pub use text::Text; @@ -114,6 +117,7 @@ where _layout: Layout<'_>, _cursor_position: Point, _messages: &mut Vec<Message>, + _renderer: &Renderer, ) { } } diff --git a/native/src/widget/button.rs b/native/src/widget/button.rs index 4ab59f7f..cf5dba93 100644 --- a/native/src/widget/button.rs +++ b/native/src/widget/button.rs @@ -31,6 +31,7 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<Message>, + _renderer: &Renderer, ) { match event { Event::Mouse(mouse::Event::Input { diff --git a/native/src/widget/checkbox.rs b/native/src/widget/checkbox.rs index 5393417e..b8053238 100644 --- a/native/src/widget/checkbox.rs +++ b/native/src/widget/checkbox.rs @@ -20,6 +20,7 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<Message>, + _renderer: &Renderer, ) { match event { Event::Mouse(mouse::Event::Input { diff --git a/native/src/widget/column.rs b/native/src/widget/column.rs index 7995cf5d..086d05ef 100644 --- a/native/src/widget/column.rs +++ b/native/src/widget/column.rs @@ -55,12 +55,17 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<Message>, + renderer: &Renderer, ) { self.children.iter_mut().zip(layout.children()).for_each( |(child, layout)| { - child - .widget - .on_event(event, layout, cursor_position, messages) + child.widget.on_event( + event, + layout, + cursor_position, + messages, + renderer, + ) }, ); } diff --git a/native/src/widget/radio.rs b/native/src/widget/radio.rs index 27b8f8a8..93ec4a36 100644 --- a/native/src/widget/radio.rs +++ b/native/src/widget/radio.rs @@ -21,6 +21,7 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<Message>, + _renderer: &Renderer, ) { match event { Event::Mouse(mouse::Event::Input { diff --git a/native/src/widget/row.rs b/native/src/widget/row.rs index 5ec27159..7dbfb92a 100644 --- a/native/src/widget/row.rs +++ b/native/src/widget/row.rs @@ -55,12 +55,17 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<Message>, + renderer: &Renderer, ) { self.children.iter_mut().zip(layout.children()).for_each( |(child, layout)| { - child - .widget - .on_event(event, layout, cursor_position, messages) + child.widget.on_event( + event, + layout, + cursor_position, + messages, + renderer, + ) }, ); } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs new file mode 100644 index 00000000..de4c749c --- /dev/null +++ b/native/src/widget/scrollable.rs @@ -0,0 +1,204 @@ +use crate::{ + column, + input::{mouse, ButtonState}, + Element, Event, Hasher, Layout, Node, Point, Rectangle, Style, Widget, +}; + +pub use iced_core::scrollable::State; + +use std::hash::Hash; + +/// A scrollable [`Column`]. +/// +/// [`Column`]: ../column/struct.Column.html +pub type Scrollable<'a, Message, Renderer> = + iced_core::Scrollable<'a, Element<'a, Message, Renderer>>; + +impl<'a, Message, Renderer> Widget<Message, Renderer> + for Scrollable<'a, Message, Renderer> +where + Renderer: self::Renderer + column::Renderer, +{ + fn node(&self, renderer: &Renderer) -> Node { + let mut content = self.content.node(renderer); + + { + let mut style = content.0.style(); + style.flex_shrink = 0.0; + + content.0.set_style(style); + } + + let mut style = Style::default() + .width(self.content.width) + .max_width(self.content.max_width) + .height(self.height) + .max_height(self.max_height) + .align_self(self.align_self); + + style.0.flex_direction = stretch::style::FlexDirection::Column; + + Node::with_children(style, vec![content]) + } + + fn on_event( + &mut self, + event: Event, + layout: Layout<'_>, + cursor_position: Point, + messages: &mut Vec<Message>, + renderer: &Renderer, + ) { + let bounds = layout.bounds(); + let is_mouse_over = bounds.contains(cursor_position); + + let content = layout.children().next().unwrap(); + let content_bounds = content.bounds(); + + let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar( + bounds, + content_bounds, + cursor_position, + ); + + // TODO: Event capture. Nested scrollables should capture scroll events. + if is_mouse_over { + match event { + Event::Mouse(mouse::Event::WheelScrolled { delta }) => { + match delta { + mouse::ScrollDelta::Lines { y, .. } => { + // TODO: Configurable speed (?) + self.state.scroll(y * 15.0, bounds, content_bounds); + } + mouse::ScrollDelta::Pixels { y, .. } => { + self.state.scroll(y, bounds, content_bounds); + } + } + } + _ => {} + } + } + + if self.state.is_scrollbar_grabbed() || is_mouse_over_scrollbar { + match event { + Event::Mouse(mouse::Event::Input { + button: mouse::Button::Left, + state, + }) => match state { + ButtonState::Pressed => { + self.state.scroll_to( + cursor_position.y / (bounds.y + bounds.height), + bounds, + content_bounds, + ); + + self.state.scrollbar_grabbed_at = Some(cursor_position); + } + ButtonState::Released => { + self.state.scrollbar_grabbed_at = None; + } + }, + Event::Mouse(mouse::Event::CursorMoved { .. }) => { + if let Some(scrollbar_grabbed_at) = + self.state.scrollbar_grabbed_at + { + let ratio = content_bounds.height / bounds.height; + let delta = scrollbar_grabbed_at.y - cursor_position.y; + + self.state.scroll( + delta * ratio, + bounds, + content_bounds, + ); + + self.state.scrollbar_grabbed_at = Some(cursor_position); + } + } + _ => {} + } + } + + let cursor_position = if is_mouse_over + && !(is_mouse_over_scrollbar + || self.state.scrollbar_grabbed_at.is_some()) + { + Point::new( + cursor_position.x, + cursor_position.y + + self.state.offset(bounds, content_bounds) as f32, + ) + } else { + // TODO: Make `cursor_position` an `Option<Point>` so we can encode + // cursor availability. + // This will probably happen naturally once we add multi-window + // support. + Point::new(cursor_position.x, -1.0) + }; + + self.content.on_event( + event, + content, + cursor_position, + messages, + renderer, + ) + } + + fn draw( + &self, + renderer: &mut Renderer, + layout: Layout<'_>, + cursor_position: Point, + ) -> Renderer::Output { + let bounds = layout.bounds(); + let content_layout = layout.children().next().unwrap(); + + self::Renderer::draw( + renderer, + &self, + bounds, + content_layout, + cursor_position, + ) + } + + fn hash_layout(&self, state: &mut Hasher) { + std::any::TypeId::of::<Scrollable<'static, (), ()>>().hash(state); + + self.height.hash(state); + self.max_height.hash(state); + self.align_self.hash(state); + + self.content.hash_layout(state) + } +} + +pub trait Renderer: crate::Renderer + Sized { + fn is_mouse_over_scrollbar( + &self, + bounds: Rectangle, + content_bounds: Rectangle, + cursor_position: Point, + ) -> bool; + + fn draw<Message>( + &mut self, + scrollable: &Scrollable<'_, Message, Self>, + bounds: Rectangle, + content_layout: Layout<'_>, + cursor_position: Point, + ) -> Self::Output; +} + +impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>> + for Element<'a, Message, Renderer> +where + Renderer: 'a + self::Renderer + column::Renderer, + Message: 'static, +{ + fn from( + scrollable: Scrollable<'a, Message, Renderer>, + ) -> Element<'a, Message, Renderer> { + Element::new(scrollable) + } +} diff --git a/native/src/widget/slider.rs b/native/src/widget/slider.rs index d643d902..be2b9b22 100644 --- a/native/src/widget/slider.rs +++ b/native/src/widget/slider.rs @@ -25,6 +25,7 @@ where layout: Layout<'_>, cursor_position: Point, messages: &mut Vec<Message>, + _renderer: &Renderer, ) { let mut change = || { let bounds = layout.bounds(); |