Draw scrollbar in Widget::draw for Scrollable
This commit is contained in:
parent
a4f4d83161
commit
54a9a232f8
11 changed files with 132 additions and 196 deletions
|
|
@ -395,9 +395,7 @@ where
|
|||
/// able to use a [`Menu`] in your user interface.
|
||||
///
|
||||
/// [renderer]: crate::renderer
|
||||
pub trait Renderer:
|
||||
scrollable::Renderer + container::Renderer + text::Renderer
|
||||
{
|
||||
pub trait Renderer: container::Renderer + text::Renderer {
|
||||
/// The [`Menu`] style supported by this renderer.
|
||||
type Style: Default + Clone;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,22 +75,6 @@ impl text::Renderer for Null {
|
|||
}
|
||||
}
|
||||
|
||||
impl scrollable::Renderer for Null {
|
||||
type Style = ();
|
||||
|
||||
fn scrollbar(
|
||||
&self,
|
||||
_bounds: Rectangle,
|
||||
_content_bounds: Rectangle,
|
||||
_offset: u32,
|
||||
_scrollbar_width: u16,
|
||||
_scrollbar_margin: u16,
|
||||
_scroller_width: u16,
|
||||
) -> Option<scrollable::Scrollbar> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl text_input::Renderer for Null {
|
||||
type Style = ();
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ use crate::layout;
|
|||
use crate::mouse;
|
||||
use crate::overlay;
|
||||
use crate::overlay::menu::{self, Menu};
|
||||
use crate::scrollable;
|
||||
use crate::text;
|
||||
use crate::touch;
|
||||
use crate::{
|
||||
|
|
@ -145,7 +144,7 @@ where
|
|||
T: Clone + ToString + Eq,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'static,
|
||||
Renderer: self::Renderer + scrollable::Renderer + 'a,
|
||||
Renderer: self::Renderer + 'a,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
|
|
|
|||
|
|
@ -3,18 +3,21 @@ use crate::event::{self, Event};
|
|||
use crate::layout;
|
||||
use crate::mouse;
|
||||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::touch;
|
||||
use crate::{
|
||||
Alignment, Clipboard, Column, Element, Hasher, Layout, Length, Padding,
|
||||
Point, Rectangle, Size, Vector, Widget,
|
||||
Alignment, Background, Clipboard, Color, Column, Element, Hasher, Layout,
|
||||
Length, Padding, Point, Rectangle, Size, Vector, Widget,
|
||||
};
|
||||
|
||||
use std::{f32, hash::Hash, u32};
|
||||
|
||||
pub use iced_style::scrollable::StyleSheet;
|
||||
|
||||
/// A widget that can vertically display an infinite amount of content with a
|
||||
/// scrollbar.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Scrollable<'a, Message, Renderer: self::Renderer> {
|
||||
pub struct Scrollable<'a, Message, Renderer> {
|
||||
state: &'a mut State,
|
||||
height: Length,
|
||||
max_height: u32,
|
||||
|
|
@ -23,10 +26,10 @@ pub struct Scrollable<'a, Message, Renderer: self::Renderer> {
|
|||
scroller_width: u16,
|
||||
content: Column<'a, Message, Renderer>,
|
||||
on_scroll: Option<Box<dyn Fn(f32) -> Message>>,
|
||||
style: Renderer::Style,
|
||||
style_sheet: &'a dyn StyleSheet,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
|
||||
impl<'a, Message, Renderer: crate::Renderer> Scrollable<'a, Message, Renderer> {
|
||||
/// Creates a new [`Scrollable`] with the given [`State`].
|
||||
pub fn new(state: &'a mut State) -> Self {
|
||||
Scrollable {
|
||||
|
|
@ -38,7 +41,7 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
|
|||
scroller_width: 10,
|
||||
content: Column::new(),
|
||||
on_scroll: None,
|
||||
style: Renderer::Style::default(),
|
||||
style_sheet: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,8 +122,11 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
|
|||
}
|
||||
|
||||
/// Sets the style of the [`Scrollable`] .
|
||||
pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
|
||||
self.style = style.into();
|
||||
pub fn style<'b>(mut self, style_sheet: &'b dyn StyleSheet) -> Self
|
||||
where
|
||||
'b: 'a,
|
||||
{
|
||||
self.style_sheet = style_sheet;
|
||||
self
|
||||
}
|
||||
|
||||
|
|
@ -150,12 +156,63 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
|
|||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn scrollbar(
|
||||
&self,
|
||||
bounds: Rectangle,
|
||||
content_bounds: Rectangle,
|
||||
) -> Option<Scrollbar> {
|
||||
let offset = self.state.offset(bounds, content_bounds);
|
||||
|
||||
if content_bounds.height > bounds.height {
|
||||
let outer_width = self.scrollbar_width.max(self.scroller_width)
|
||||
+ 2 * self.scrollbar_margin;
|
||||
|
||||
let outer_bounds = Rectangle {
|
||||
x: bounds.x + bounds.width - outer_width as f32,
|
||||
y: bounds.y,
|
||||
width: outer_width as f32,
|
||||
height: bounds.height,
|
||||
};
|
||||
|
||||
let scrollbar_bounds = Rectangle {
|
||||
x: bounds.x + bounds.width
|
||||
- f32::from(outer_width / 2 + self.scrollbar_width / 2),
|
||||
y: bounds.y,
|
||||
width: self.scrollbar_width as f32,
|
||||
height: bounds.height,
|
||||
};
|
||||
|
||||
let ratio = bounds.height / content_bounds.height;
|
||||
let scroller_height = bounds.height * ratio;
|
||||
let y_offset = offset as f32 * ratio;
|
||||
|
||||
let scroller_bounds = Rectangle {
|
||||
x: bounds.x + bounds.width
|
||||
- f32::from(outer_width / 2 + self.scroller_width / 2),
|
||||
y: scrollbar_bounds.y + y_offset,
|
||||
width: self.scroller_width as f32,
|
||||
height: scroller_height,
|
||||
};
|
||||
|
||||
Some(Scrollbar {
|
||||
outer_bounds,
|
||||
bounds: scrollbar_bounds,
|
||||
margin: self.scrollbar_margin,
|
||||
scroller: Scroller {
|
||||
bounds: scroller_bounds,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
for Scrollable<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: self::Renderer,
|
||||
Renderer: crate::Renderer,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
Widget::<Message, Renderer>::width(&self.content)
|
||||
|
|
@ -201,15 +258,7 @@ where
|
|||
let content = layout.children().next().unwrap();
|
||||
let content_bounds = content.bounds();
|
||||
|
||||
let offset = self.state.offset(bounds, content_bounds);
|
||||
let scrollbar = renderer.scrollbar(
|
||||
bounds,
|
||||
content_bounds,
|
||||
offset,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
);
|
||||
let scrollbar = self.scrollbar(bounds, content_bounds);
|
||||
let is_mouse_over_scrollbar = scrollbar
|
||||
.as_ref()
|
||||
.map(|scrollbar| scrollbar.is_mouse_over(cursor_position))
|
||||
|
|
@ -385,14 +434,7 @@ where
|
|||
let content_layout = layout.children().next().unwrap();
|
||||
let content_bounds = content_layout.bounds();
|
||||
let offset = self.state.offset(bounds, content_bounds);
|
||||
let scrollbar = renderer.scrollbar(
|
||||
bounds,
|
||||
content_bounds,
|
||||
offset,
|
||||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
);
|
||||
let scrollbar = self.scrollbar(bounds, content_bounds);
|
||||
|
||||
let is_mouse_over = bounds.contains(cursor_position);
|
||||
let is_mouse_over_scrollbar = scrollbar
|
||||
|
|
@ -420,43 +462,43 @@ where
|
|||
);
|
||||
});
|
||||
|
||||
// TODO: Draw scroller
|
||||
// let style = if self.state.is_scroller_grabbed() {
|
||||
// style_sheet.dragging()
|
||||
// } else if is_mouse_over_scrollbar {
|
||||
// style_sheet.hovered()
|
||||
// } else {
|
||||
// style_sheet.active()
|
||||
// };
|
||||
let style = if self.state.is_scroller_grabbed() {
|
||||
self.style_sheet.dragging()
|
||||
} else if is_mouse_over_scrollbar {
|
||||
self.style_sheet.hovered()
|
||||
} else {
|
||||
self.style_sheet.active()
|
||||
};
|
||||
|
||||
// let is_scrollbar_visible =
|
||||
// style.background.is_some() || style.border_width > 0.0;
|
||||
let is_scrollbar_visible =
|
||||
style.background.is_some() || style.border_width > 0.0;
|
||||
|
||||
// if is_mouse_over
|
||||
// || self.state.is_scroller_grabbed()
|
||||
// || is_scrollbar_visible
|
||||
// {
|
||||
// // Primitive::Quad {
|
||||
// // bounds: scrollbar.scroller.bounds,
|
||||
// // background: Background::Color(style.scroller.color),
|
||||
// // border_radius: style.scroller.border_radius,
|
||||
// // border_width: style.scroller.border_width,
|
||||
// // border_color: style.scroller.border_color,
|
||||
// // }
|
||||
// };
|
||||
renderer.with_layer(bounds, Vector::new(0, 0), |renderer| {
|
||||
if is_scrollbar_visible {
|
||||
renderer.fill_rectangle(renderer::Quad {
|
||||
bounds: scrollbar.bounds,
|
||||
background: style
|
||||
.background
|
||||
.unwrap_or(Background::Color(Color::TRANSPARENT)),
|
||||
border_radius: style.border_radius,
|
||||
border_width: style.border_width,
|
||||
border_color: style.border_color,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Draw scrollbar
|
||||
// if is_scrollbar_visible {
|
||||
// Primitive::Quad {
|
||||
// bounds: scrollbar.bounds,
|
||||
// background: style
|
||||
// .background
|
||||
// .unwrap_or(Background::Color(Color::TRANSPARENT)),
|
||||
// border_radius: style.border_radius,
|
||||
// border_width: style.border_width,
|
||||
// border_color: style.border_color,
|
||||
// }
|
||||
//}
|
||||
if is_mouse_over
|
||||
|| self.state.is_scroller_grabbed()
|
||||
|| is_scrollbar_visible
|
||||
{
|
||||
renderer.fill_rectangle(renderer::Quad {
|
||||
bounds: scrollbar.scroller.bounds,
|
||||
background: Background::Color(style.scroller.color),
|
||||
border_radius: style.scroller.border_radius,
|
||||
border_width: style.scroller.border_width,
|
||||
border_color: style.scroller.border_color,
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
self.content.draw(
|
||||
renderer,
|
||||
|
|
@ -614,19 +656,19 @@ impl State {
|
|||
|
||||
/// The scrollbar of a [`Scrollable`].
|
||||
#[derive(Debug)]
|
||||
pub struct Scrollbar {
|
||||
struct Scrollbar {
|
||||
/// The outer bounds of the scrollable, including the [`Scrollbar`] and
|
||||
/// [`Scroller`].
|
||||
pub outer_bounds: Rectangle,
|
||||
outer_bounds: Rectangle,
|
||||
|
||||
/// The bounds of the [`Scrollbar`].
|
||||
pub bounds: Rectangle,
|
||||
bounds: Rectangle,
|
||||
|
||||
/// The margin within the [`Scrollbar`].
|
||||
pub margin: u16,
|
||||
margin: u16,
|
||||
|
||||
/// The bounds of the [`Scroller`].
|
||||
pub scroller: Scroller,
|
||||
scroller: Scroller,
|
||||
}
|
||||
|
||||
impl Scrollbar {
|
||||
|
|
@ -661,38 +703,15 @@ impl Scrollbar {
|
|||
|
||||
/// The handle of a [`Scrollbar`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct Scroller {
|
||||
struct Scroller {
|
||||
/// The bounds of the [`Scroller`].
|
||||
pub bounds: Rectangle,
|
||||
}
|
||||
|
||||
/// The renderer of a [`Scrollable`].
|
||||
///
|
||||
/// Your [renderer] will need to implement this trait before being
|
||||
/// able to use a [`Scrollable`] in your user interface.
|
||||
///
|
||||
/// [renderer]: crate::renderer
|
||||
pub trait Renderer: crate::Renderer + Sized {
|
||||
/// The style supported by this renderer.
|
||||
type Style: Default;
|
||||
|
||||
/// Returns the [`Scrollbar`] given the bounds and content bounds of a
|
||||
/// [`Scrollable`].
|
||||
fn scrollbar(
|
||||
&self,
|
||||
bounds: Rectangle,
|
||||
content_bounds: Rectangle,
|
||||
offset: u32,
|
||||
scrollbar_width: u16,
|
||||
scrollbar_margin: u16,
|
||||
scroller_width: u16,
|
||||
) -> Option<Scrollbar>;
|
||||
bounds: Rectangle,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Scrollable<'a, Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: 'a + self::Renderer,
|
||||
Renderer: 'a + crate::Renderer,
|
||||
Message: 'a,
|
||||
{
|
||||
fn from(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue