Move widgets from core to native and web

Also made fields private and improved `Renderer` traits.
This commit is contained in:
Héctor Ramón Jiménez 2019-11-21 13:47:20 +01:00
parent d3553adf27
commit 65eb218d3d
59 changed files with 2455 additions and 1942 deletions

View file

@ -1,20 +1,104 @@
use crate::{
column,
input::{mouse, ButtonState},
layout, Element, Event, Hasher, Layout, Length, Point, Rectangle, Size,
Widget,
layout, Align, Column, Element, Event, Hasher, Layout, Length, Point,
Rectangle, Size, Widget,
};
pub use iced_core::scrollable::State;
use std::{f32, hash::Hash, u32};
use std::f32;
use std::hash::Hash;
/// A widget that can vertically display an infinite amount of content with a
/// scrollbar.
pub struct Scrollable<'a, Message, Renderer> {
state: &'a mut State,
height: Length,
max_height: u32,
content: Column<'a, Message, Renderer>,
}
/// 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> Scrollable<'a, Message, Renderer> {
/// Creates a new [`Scrollable`] with the given [`State`].
///
/// [`Scrollable`]: struct.Scrollable.html
/// [`State`]: struct.State.html
pub fn new(state: &'a mut State) -> Self {
Scrollable {
state,
height: Length::Shrink,
max_height: u32::MAX,
content: Column::new(),
}
}
/// Sets the vertical spacing _between_ elements.
///
/// Custom margins per element do not exist in Iced. You should use this
/// method instead! While less flexible, it helps you keep spacing between
/// elements consistent.
pub fn spacing(mut self, units: u16) -> Self {
self.content = self.content.spacing(units);
self
}
/// Sets the padding of the [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn padding(mut self, units: u16) -> Self {
self.content = self.content.padding(units);
self
}
/// Sets the width of the [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn width(mut self, width: Length) -> Self {
self.content = self.content.width(width);
self
}
/// Sets the height of the [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
/// Sets the maximum width of the [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn max_width(mut self, max_width: u32) -> Self {
self.content = self.content.max_width(max_width);
self
}
/// Sets the maximum height of the [`Scrollable`] in pixels.
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn max_height(mut self, max_height: u32) -> Self {
self.max_height = max_height;
self
}
/// Sets the horizontal alignment of the contents of the [`Scrollable`] .
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn align_items(mut self, align_items: Align) -> Self {
self.content = self.content.align_items(align_items);
self
}
/// Adds an element to the [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn push<E>(mut self, child: E) -> Self
where
E: Into<Element<'a, Message, Renderer>>,
{
self.content = self.content.push(child);
self
}
}
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Scrollable<'a, Message, Renderer>
@ -153,13 +237,35 @@ where
) -> Renderer::Output {
let bounds = layout.bounds();
let content_layout = layout.children().next().unwrap();
let content_bounds = content_layout.bounds();
let offset = self.state.offset(bounds, content_bounds);
let is_mouse_over = bounds.contains(cursor_position);
let is_mouse_over_scrollbar = renderer.is_mouse_over_scrollbar(
bounds,
content_bounds,
cursor_position,
);
let content = {
let cursor_position = if is_mouse_over && !is_mouse_over_scrollbar {
Point::new(cursor_position.x, cursor_position.y + offset as f32)
} else {
Point::new(cursor_position.x, -1.0)
};
self.content.draw(renderer, content_layout, cursor_position)
};
self::Renderer::draw(
renderer,
&self,
&self.state,
bounds,
content_layout,
cursor_position,
content_layout.bounds(),
is_mouse_over,
is_mouse_over_scrollbar,
offset,
content,
)
}
@ -173,6 +279,80 @@ where
}
}
/// The local state of a [`Scrollable`].
///
/// [`Scrollable`]: struct.Scrollable.html
#[derive(Debug, Clone, Copy, Default)]
pub struct State {
scrollbar_grabbed_at: Option<Point>,
offset: u32,
}
impl State {
/// Creates a new [`State`] with the scrollbar located at the top.
///
/// [`State`]: struct.State.html
pub fn new() -> Self {
State::default()
}
/// Apply a scrolling offset to the current [`State`], given the bounds of
/// the [`Scrollable`] and its contents.
///
/// [`Scrollable`]: struct.Scrollable.html
/// [`State`]: struct.State.html
pub fn scroll(
&mut self,
delta_y: f32,
bounds: Rectangle,
content_bounds: Rectangle,
) {
if bounds.height >= content_bounds.height {
return;
}
self.offset = (self.offset as i32 - delta_y.round() as i32)
.max(0)
.min((content_bounds.height - bounds.height) as i32)
as u32;
}
/// Moves the scroll position to a relative amount, given the bounds of
/// the [`Scrollable`] and its contents.
///
/// `0` represents scrollbar at the top, while `1` represents scrollbar at
/// the bottom.
///
/// [`Scrollable`]: struct.Scrollable.html
/// [`State`]: struct.State.html
pub fn scroll_to(
&mut self,
percentage: f32,
bounds: Rectangle,
content_bounds: Rectangle,
) {
self.offset = ((content_bounds.height - bounds.height) * percentage)
.max(0.0) as u32;
}
/// Returns the current scrolling offset of the [`State`], given the bounds
/// of the [`Scrollable`] and its contents.
///
/// [`Scrollable`]: struct.Scrollable.html
/// [`State`]: struct.State.html
pub fn offset(&self, bounds: Rectangle, content_bounds: Rectangle) -> u32 {
let hidden_content =
(content_bounds.height - bounds.height).max(0.0).round() as u32;
self.offset.min(hidden_content)
}
/// Returns whether the scrollbar is currently grabbed or not.
pub fn is_scrollbar_grabbed(&self) -> bool {
self.scrollbar_grabbed_at.is_some()
}
}
pub trait Renderer: crate::Renderer + Sized {
fn is_mouse_over_scrollbar(
&self,
@ -181,12 +361,15 @@ pub trait Renderer: crate::Renderer + Sized {
cursor_position: Point,
) -> bool;
fn draw<Message>(
fn draw(
&mut self,
scrollable: &Scrollable<'_, Message, Self>,
scrollable: &State,
bounds: Rectangle,
content_layout: Layout<'_>,
cursor_position: Point,
content_bounds: Rectangle,
is_mouse_over: bool,
is_mouse_over_scrollbar: bool,
offset: u32,
content: Self::Output,
) -> Self::Output;
}