diff options
-rw-r--r-- | examples/scrollable/src/main.rs | 2 | ||||
-rw-r--r-- | examples/scrollable/src/style.rs | 4 | ||||
-rw-r--r-- | examples/styling/src/main.rs | 6 | ||||
-rw-r--r-- | graphics/src/widget/scrollable.rs | 64 | ||||
-rw-r--r-- | native/Cargo.toml | 4 | ||||
-rw-r--r-- | native/src/overlay/menu.rs | 4 | ||||
-rw-r--r-- | native/src/renderer/null.rs | 16 | ||||
-rw-r--r-- | native/src/widget/pick_list.rs | 3 | ||||
-rw-r--r-- | native/src/widget/scrollable.rs | 207 | ||||
-rw-r--r-- | style/src/scrollable.rs | 13 | ||||
-rw-r--r-- | web/src/widget/scrollable.rs | 11 |
11 files changed, 135 insertions, 199 deletions
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index 3416b83d..272f0ce2 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -95,7 +95,7 @@ impl Sandbox for ScrollableDemo { .on_scroll(move |offset| { Message::Scrolled(i, offset) }) - .style(*theme) + .style(theme.clone().into()) .push(Text::new(variant.title)) .push( Button::new( diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs index ae449141..d955f52d 100644 --- a/examples/scrollable/src/style.rs +++ b/examples/scrollable/src/style.rs @@ -34,11 +34,11 @@ impl From<Theme> for Box<dyn radio::StyleSheet> { } } -impl From<Theme> for Box<dyn scrollable::StyleSheet> { +impl From<Theme> for &'static dyn scrollable::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Scrollable.into(), + Theme::Dark => &dark::Scrollable, } } } diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs index 81d33ad3..1746d7b4 100644 --- a/examples/styling/src/main.rs +++ b/examples/styling/src/main.rs @@ -98,7 +98,7 @@ impl Sandbox for Styling { let scrollable = Scrollable::new(&mut self.scroll) .width(Length::Fill) .height(Length::Units(100)) - .style(self.theme) + .style(self.theme.into()) .push(Text::new("Scroll me!")) .push(Space::with_height(Length::Units(800))) .push(Text::new("You did it!")); @@ -212,11 +212,11 @@ mod style { } } - impl From<Theme> for Box<dyn scrollable::StyleSheet> { + impl From<Theme> for &'static dyn scrollable::StyleSheet { fn from(theme: Theme) -> Self { match theme { Theme::Light => Default::default(), - Theme::Dark => dark::Scrollable.into(), + Theme::Dark => &dark::Scrollable, } } } diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index f1fe0d2d..61eae587 100644 --- a/graphics/src/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -1,7 +1,5 @@ //! Navigate an endless amount of content with a scrollbar. -use crate::{Backend, Renderer}; -use iced_native::scrollable; -use iced_native::Rectangle; +use crate::Renderer; pub use iced_native::scrollable::State; pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; @@ -13,63 +11,3 @@ pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; /// `Renderer`. pub type Scrollable<'a, Message, Backend> = iced_native::Scrollable<'a, Message, Renderer<Backend>>; - -impl<B> scrollable::Renderer for Renderer<B> -where - B: Backend, -{ - type Style = Box<dyn iced_style::scrollable::StyleSheet>; - - fn scrollbar( - &self, - bounds: Rectangle, - content_bounds: Rectangle, - offset: u32, - scrollbar_width: u16, - scrollbar_margin: u16, - scroller_width: u16, - ) -> Option<scrollable::Scrollbar> { - if content_bounds.height > bounds.height { - let outer_width = - scrollbar_width.max(scroller_width) + 2 * 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 + scrollbar_width / 2), - y: bounds.y, - width: 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 + scroller_width / 2), - y: scrollbar_bounds.y + y_offset, - width: scroller_width as f32, - height: scroller_height, - }; - - Some(scrollable::Scrollbar { - outer_bounds, - bounds: scrollbar_bounds, - margin: scrollbar_margin, - scroller: scrollable::Scroller { - bounds: scroller_bounds, - }, - }) - } else { - None - } - } -} diff --git a/native/Cargo.toml b/native/Cargo.toml index a3134ef4..5de99b2e 100644 --- a/native/Cargo.toml +++ b/native/Cargo.toml @@ -23,3 +23,7 @@ path = "../core" version = "0.3" path = "../futures" features = ["thread-pool"] + +[dependencies.iced_style] +version = "0.3" +path = "../style" diff --git a/native/src/overlay/menu.rs b/native/src/overlay/menu.rs index f90a9f7b..c4f64f85 100644 --- a/native/src/overlay/menu.rs +++ b/native/src/overlay/menu.rs @@ -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; } diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 1c026fde..960a5727 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -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 = (); diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs index 7154a572..73b01b01 100644 --- a/native/src/widget/pick_list.rs +++ b/native/src/widget/pick_list.rs @@ -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 diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index ba3d3dd6..98357928 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -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 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, - // // } - // }; - - // 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, - // } - //} + 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; + + 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, + }); + } + + 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( diff --git a/style/src/scrollable.rs b/style/src/scrollable.rs index 65da9803..741d9d39 100644 --- a/style/src/scrollable.rs +++ b/style/src/scrollable.rs @@ -60,17 +60,8 @@ impl StyleSheet for Default { } } -impl std::default::Default for Box<dyn StyleSheet> { +impl std::default::Default for &'static dyn StyleSheet { fn default() -> Self { - Box::new(Default) - } -} - -impl<T> From<T> for Box<dyn StyleSheet> -where - T: 'static + StyleSheet, -{ - fn from(style: T) -> Self { - Box::new(style) + &Default } } diff --git a/web/src/widget/scrollable.rs b/web/src/widget/scrollable.rs index 847bf5a0..595eb26e 100644 --- a/web/src/widget/scrollable.rs +++ b/web/src/widget/scrollable.rs @@ -14,7 +14,7 @@ pub struct Scrollable<'a, Message> { max_height: u32, content: Column<'a, Message>, #[allow(dead_code)] - style: Box<dyn StyleSheet>, + style_sheet: &'a dyn StyleSheet, } impl<'a, Message> Scrollable<'a, Message> { @@ -27,7 +27,7 @@ impl<'a, Message> Scrollable<'a, Message> { height: Length::Shrink, max_height: u32::MAX, content: Column::new(), - style: Default::default(), + style_sheet: Default::default(), } } @@ -78,8 +78,11 @@ impl<'a, Message> Scrollable<'a, Message> { } /// Sets the style of the [`Scrollable`] . - pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> 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 } |