First implementation

This commit is contained in:
Friz64 2019-11-25 20:27:15 +01:00 committed by Friz64
parent e404f5098a
commit 5eec3a8867
5 changed files with 119 additions and 51 deletions

View file

@ -145,7 +145,7 @@ impl Steps {
Step::Debugger, Step::Debugger,
Step::End, Step::End,
], ],
current: 0, current: 6,
} }
} }

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
button, checkbox, column, radio, row, scrollable, text, text_input, button, checkbox, column, radio, row, scrollable, text, text_input,
Background, Color, Element, Font, HorizontalAlignment, Layout, Point, Background, Color, Element, Font, HorizontalAlignment, Layout, Point,
Rectangle, Renderer, Size, VerticalAlignment, Rectangle, Renderer, ScrollbarGrab, Size, VerticalAlignment,
}; };
/// A renderer that does nothing. /// A renderer that does nothing.
@ -61,13 +61,14 @@ impl text::Renderer for Null {
} }
impl scrollable::Renderer for Null { impl scrollable::Renderer for Null {
fn is_mouse_over_scrollbar( fn scrollbar_grab(
&self, &self,
_bounds: Rectangle, _bounds: Rectangle,
_content_bounds: Rectangle, _content_bounds: Rectangle,
_offset: u32,
_cursor_position: Point, _cursor_position: Point,
) -> bool { ) -> Option<ScrollbarGrab> {
false None
} }
fn draw( fn draw(

View file

@ -47,7 +47,7 @@ pub use radio::Radio;
#[doc(no_inline)] #[doc(no_inline)]
pub use row::Row; pub use row::Row;
#[doc(no_inline)] #[doc(no_inline)]
pub use scrollable::Scrollable; pub use scrollable::{Scrollable, ScrollbarGrab};
#[doc(no_inline)] #[doc(no_inline)]
pub use slider::Slider; pub use slider::Slider;
#[doc(no_inline)] #[doc(no_inline)]

View file

@ -150,9 +150,11 @@ where
let content = layout.children().next().unwrap(); let content = layout.children().next().unwrap();
let content_bounds = content.bounds(); let content_bounds = content.bounds();
let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar( let offset = self.state.offset(bounds, content_bounds);
let scrollbar_grab = renderer.scrollbar_grab(
bounds, bounds,
content_bounds, content_bounds,
offset,
cursor_position, cursor_position,
); );
@ -174,25 +176,45 @@ where
} }
} }
if self.state.is_scrollbar_grabbed() || is_mouse_over_scrollbar { if self.state.currently_grabbed() || scrollbar_grab.is_some() {
match event { match event {
Event::Mouse(mouse::Event::Input { Event::Mouse(mouse::Event::Input {
button: mouse::Button::Left, button: mouse::Button::Left,
state, state,
}) => match state { }) => match state {
ButtonState::Pressed => { ButtonState::Pressed => {
self.state.scroll_to( let (scrollbar_grab, scroller_bounds) =
cursor_position.y / (bounds.y + bounds.height), scrollbar_grab.unwrap();
let scroller_grabbed_at = match scrollbar_grab {
ScrollbarGrab::Background => 0.5,
ScrollbarGrab::Scroller => {
(cursor_position.y - scroller_bounds.y)
/ scroller_bounds.height
}
};
let scroll_percentage = (cursor_position.y
- (scroller_bounds.height * scroller_grabbed_at))
/ (bounds.height
- (scroller_bounds.height
* scroller_grabbed_at));
dbg!((scroll_percentage, scroller_grabbed_at));
/*self.state.scroll_to(
scroll_percentage,
bounds, bounds,
content_bounds, content_bounds,
); );*/
self.state.scrollbar_grabbed_at = Some(cursor_position); self.state.scroller_grabbed_at =
Some(scroller_grabbed_at);
} }
ButtonState::Released => { ButtonState::Released => {
self.state.scrollbar_grabbed_at = None; self.state.scroller_grabbed_at = None;
} }
}, },
/* TODO: Implement dragging to scroll
Event::Mouse(mouse::Event::CursorMoved { .. }) => { Event::Mouse(mouse::Event::CursorMoved { .. }) => {
if let Some(scrollbar_grabbed_at) = if let Some(scrollbar_grabbed_at) =
self.state.scrollbar_grabbed_at self.state.scrollbar_grabbed_at
@ -209,13 +231,13 @@ where
self.state.scrollbar_grabbed_at = Some(cursor_position); self.state.scrollbar_grabbed_at = Some(cursor_position);
} }
} }
*/
_ => {} _ => {}
} }
} }
let cursor_position = if is_mouse_over let cursor_position = if is_mouse_over
&& !(is_mouse_over_scrollbar && !(scrollbar_grab.is_some() || self.state.currently_grabbed())
|| self.state.scrollbar_grabbed_at.is_some())
{ {
Point::new( Point::new(
cursor_position.x, cursor_position.x,
@ -251,11 +273,9 @@ where
let offset = self.state.offset(bounds, content_bounds); let offset = self.state.offset(bounds, content_bounds);
let is_mouse_over = bounds.contains(cursor_position); let is_mouse_over = bounds.contains(cursor_position);
let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar( let is_mouse_over_scrollbar = renderer
bounds, .scrollbar_grab(bounds, content_bounds, offset, cursor_position)
content_bounds, .is_some();
cursor_position,
);
let content = { let content = {
let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar { let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
@ -294,7 +314,7 @@ where
/// [`Scrollable`]: struct.Scrollable.html /// [`Scrollable`]: struct.Scrollable.html
#[derive(Debug, Clone, Copy, Default)] #[derive(Debug, Clone, Copy, Default)]
pub struct State { pub struct State {
scrollbar_grabbed_at: Option<Point>, scroller_grabbed_at: Option<f32>,
offset: f32, offset: f32,
} }
@ -357,11 +377,20 @@ impl State {
} }
/// Returns whether the scrollbar is currently grabbed or not. /// Returns whether the scrollbar is currently grabbed or not.
pub fn is_scrollbar_grabbed(&self) -> bool { pub fn currently_grabbed(&self) -> bool {
self.scrollbar_grabbed_at.is_some() self.scroller_grabbed_at.is_some()
} }
} }
#[derive(Debug, Clone, Copy)]
/// What the mouse is grabbing on the scrollbar
pub enum ScrollbarGrab {
/// The mouse is grabbing the background
Background,
/// The mouse is grabbing the scroller
Scroller,
}
/// The renderer of a [`Scrollable`]. /// The renderer of a [`Scrollable`].
/// ///
/// Your [renderer] will need to implement this trait before being /// Your [renderer] will need to implement this trait before being
@ -370,16 +399,18 @@ impl State {
/// [`Scrollable`]: struct.Scrollable.html /// [`Scrollable`]: struct.Scrollable.html
/// [renderer]: ../../renderer/index.html /// [renderer]: ../../renderer/index.html
pub trait Renderer: crate::Renderer + Sized { pub trait Renderer: crate::Renderer + Sized {
/// Returns whether the mouse is over the scrollbar given the bounds of /// Returns what part of the scrollbar is being grabbed by the mouse
/// the [`Scrollable`] and its contents. /// given the bounds of the [`Scrollable`] and its contents together
/// with the current scroller bounds.
/// ///
/// [`Scrollable`]: struct.Scrollable.html /// [`Scrollable`]: struct.Scrollable.html
fn is_mouse_over_scrollbar( fn scrollbar_grab(
&self, &self,
bounds: Rectangle, bounds: Rectangle,
content_bounds: Rectangle, content_bounds: Rectangle,
offset: u32,
cursor_position: Point, cursor_position: Point,
) -> bool; ) -> Option<(ScrollbarGrab, Rectangle)>;
/// Draws the [`Scrollable`]. /// Draws the [`Scrollable`].
/// ///

View file

@ -1,12 +1,13 @@
use crate::{Primitive, Renderer}; use crate::{Primitive, Renderer};
use iced_native::{ use iced_native::{
scrollable, Background, MouseCursor, Point, Rectangle, Vector, scrollable, Background, MouseCursor, Point, Rectangle, ScrollbarGrab,
Vector,
}; };
const SCROLLBAR_WIDTH: u16 = 10; const SCROLLBAR_WIDTH: u16 = 10;
const SCROLLBAR_MARGIN: u16 = 2; const SCROLLBAR_MARGIN: u16 = 2;
fn scrollbar_bounds(bounds: Rectangle) -> Rectangle { fn background_bounds(bounds: Rectangle) -> Rectangle {
Rectangle { Rectangle {
x: bounds.x + bounds.width x: bounds.x + bounds.width
- f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN),
@ -16,15 +17,53 @@ fn scrollbar_bounds(bounds: Rectangle) -> Rectangle {
} }
} }
fn scroller_bounds(
bounds: Rectangle,
content_bounds: Rectangle,
background_bounds: Rectangle,
offset: u32,
) -> Rectangle {
let ratio = bounds.height / content_bounds.height;
let scrollbar_height = bounds.height * ratio;
let y_offset = offset as f32 * ratio;
Rectangle {
x: background_bounds.x + f32::from(SCROLLBAR_MARGIN),
y: background_bounds.y + y_offset,
width: background_bounds.width - f32::from(2 * SCROLLBAR_MARGIN),
height: scrollbar_height,
}
}
impl scrollable::Renderer for Renderer { impl scrollable::Renderer for Renderer {
fn is_mouse_over_scrollbar( fn scrollbar_grab(
&self, &self,
bounds: Rectangle, bounds: Rectangle,
content_bounds: Rectangle, content_bounds: Rectangle,
offset: u32,
cursor_position: Point, cursor_position: Point,
) -> bool { ) -> Option<(ScrollbarGrab, Rectangle)> {
content_bounds.height > bounds.height let background_bounds = background_bounds(bounds);
&& scrollbar_bounds(bounds).contains(cursor_position) if content_bounds.height > bounds.height
&& background_bounds.contains(cursor_position)
{
let scroller_bounds = scroller_bounds(
bounds,
content_bounds,
background_bounds,
offset,
);
let scrollbar_grab = if scroller_bounds.contains(cursor_position) {
ScrollbarGrab::Scroller
} else {
ScrollbarGrab::Background
};
Some((scrollbar_grab, scroller_bounds))
} else {
None
}
} }
fn draw( fn draw(
@ -38,7 +77,7 @@ impl scrollable::Renderer for Renderer {
(content, mouse_cursor): Self::Output, (content, mouse_cursor): Self::Output,
) -> Self::Output { ) -> Self::Output {
let is_content_overflowing = content_bounds.height > bounds.height; let is_content_overflowing = content_bounds.height > bounds.height;
let scrollbar_bounds = scrollbar_bounds(bounds); let background_bounds = background_bounds(bounds);
let clip = Primitive::Clip { let clip = Primitive::Clip {
bounds, bounds,
@ -48,31 +87,28 @@ impl scrollable::Renderer for Renderer {
( (
if is_content_overflowing if is_content_overflowing
&& (is_mouse_over || state.is_scrollbar_grabbed()) && (is_mouse_over || state.currently_grabbed())
{ {
let ratio = bounds.height / content_bounds.height; let scroller_bounds = scroller_bounds(
let scrollbar_height = bounds.height * ratio; bounds,
let y_offset = offset as f32 * ratio; content_bounds,
background_bounds,
offset,
);
let scrollbar = Primitive::Quad { let scrollbar = Primitive::Quad {
bounds: Rectangle { bounds: scroller_bounds,
x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN),
y: scrollbar_bounds.y + y_offset,
width: scrollbar_bounds.width
- f32::from(2 * SCROLLBAR_MARGIN),
height: scrollbar_height,
},
background: Background::Color([0.0, 0.0, 0.0, 0.7].into()), background: Background::Color([0.0, 0.0, 0.0, 0.7].into()),
border_radius: 5, border_radius: 5,
}; };
if is_mouse_over_scrollbar || state.is_scrollbar_grabbed() { if is_mouse_over_scrollbar || state.currently_grabbed() {
let scrollbar_background = Primitive::Quad { let scrollbar_background = Primitive::Quad {
bounds: Rectangle { bounds: Rectangle {
x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), x: background_bounds.x
width: scrollbar_bounds.width + f32::from(SCROLLBAR_MARGIN),
width: background_bounds.width
- f32::from(2 * SCROLLBAR_MARGIN), - f32::from(2 * SCROLLBAR_MARGIN),
..scrollbar_bounds ..background_bounds
}, },
background: Background::Color( background: Background::Color(
[0.0, 0.0, 0.0, 0.3].into(), [0.0, 0.0, 0.0, 0.3].into(),
@ -91,7 +127,7 @@ impl scrollable::Renderer for Renderer {
} else { } else {
clip clip
}, },
if is_mouse_over_scrollbar || state.is_scrollbar_grabbed() { if is_mouse_over_scrollbar || state.currently_grabbed() {
MouseCursor::Idle MouseCursor::Idle
} else { } else {
mouse_cursor mouse_cursor