summaryrefslogtreecommitdiffstats
path: root/native
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón <hector0193@gmail.com>2019-11-03 05:19:12 +0100
committerLibravatar GitHub <noreply@github.com>2019-11-03 05:19:12 +0100
commit0ea911ae36bbde8c288f7ae1ba8a0049b696d7c4 (patch)
tree929cb493b674512520f1b6a92f86d7a09e5801f6 /native
parentde3c87b9a793c0d0799948e16ad1b14e5b4892ba (diff)
parent022dc0139b7437f167a8d3ae483bf8e83f1dab04 (diff)
downloadiced-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.toml2
-rw-r--r--native/src/element.rs13
-rw-r--r--native/src/input/mouse.rs2
-rw-r--r--native/src/input/mouse/event.rs17
-rw-r--r--native/src/user_interface.rs8
-rw-r--r--native/src/widget.rs4
-rw-r--r--native/src/widget/button.rs1
-rw-r--r--native/src/widget/checkbox.rs1
-rw-r--r--native/src/widget/column.rs11
-rw-r--r--native/src/widget/radio.rs1
-rw-r--r--native/src/widget/row.rs11
-rw-r--r--native/src/widget/scrollable.rs204
-rw-r--r--native/src/widget/slider.rs1
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();