Merge pull request #575 from clarkmoody/scrollable-width
Custom Scrollbar Width
This commit is contained in:
		
						commit
						8a3ce90959
					
				| @ -67,6 +67,7 @@ members = [ | ||||
|     "examples/pick_list", | ||||
|     "examples/pokedex", | ||||
|     "examples/progress_bar", | ||||
|     "examples/scrollable", | ||||
|     "examples/solar_system", | ||||
|     "examples/stopwatch", | ||||
|     "examples/styling", | ||||
|  | ||||
| @ -103,6 +103,7 @@ A bunch of simpler examples exist: | ||||
| - [`pick_list`](pick_list), a dropdown list of selectable options. | ||||
| - [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI]. | ||||
| - [`progress_bar`](progress_bar), a simple progress bar that can be filled by using a slider. | ||||
| - [`scrollable`](scrollable), a showcase of the various scrollbar width options. | ||||
| - [`solar_system`](solar_system), an animated solar system drawn using the `Canvas` widget and showcasing how to compose different transforms. | ||||
| - [`stopwatch`](stopwatch), a watch with start/stop and reset buttons showcasing how to listen to time. | ||||
| - [`svg`](svg), an application that renders the [Ghostscript Tiger] by leveraging the `Svg` widget. | ||||
|  | ||||
							
								
								
									
										9
									
								
								examples/scrollable/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								examples/scrollable/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| [package] | ||||
| name = "scrollable" | ||||
| version = "0.1.0" | ||||
| authors = ["Clark Moody <clark@clarkmoody.com>"] | ||||
| edition = "2018" | ||||
| publish = false | ||||
| 
 | ||||
| [dependencies] | ||||
| iced = { path = "../.." } | ||||
							
								
								
									
										15
									
								
								examples/scrollable/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								examples/scrollable/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| # Scrollable | ||||
| An example showcasing the various size and style options for the Scrollable. | ||||
| 
 | ||||
| All the example code is located in the __[`main`](src/main.rs)__ file. | ||||
| 
 | ||||
| <div align="center"> | ||||
|   <a href="./screenshot.png"> | ||||
|     <img src="./screenshot.png" height="640px"> | ||||
|   </a> | ||||
| </div> | ||||
| 
 | ||||
| You can run it with `cargo run`: | ||||
| ``` | ||||
| cargo run --package scrollable | ||||
| ``` | ||||
							
								
								
									
										
											BIN
										
									
								
								examples/scrollable/screenshot.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								examples/scrollable/screenshot.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 145 KiB | 
							
								
								
									
										184
									
								
								examples/scrollable/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								examples/scrollable/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,184 @@ | ||||
| mod style; | ||||
| 
 | ||||
| use iced::{ | ||||
|     scrollable, Column, Container, Element, Length, Radio, Row, Rule, Sandbox, | ||||
|     Scrollable, Settings, Space, Text, | ||||
| }; | ||||
| 
 | ||||
| pub fn main() -> iced::Result { | ||||
|     ScrollableDemo::run(Settings::default()) | ||||
| } | ||||
| 
 | ||||
| struct ScrollableDemo { | ||||
|     theme: style::Theme, | ||||
|     variants: Vec<Variant>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Clone)] | ||||
| enum Message { | ||||
|     ThemeChanged(style::Theme), | ||||
| } | ||||
| 
 | ||||
| impl Sandbox for ScrollableDemo { | ||||
|     type Message = Message; | ||||
| 
 | ||||
|     fn new() -> Self { | ||||
|         ScrollableDemo { | ||||
|             theme: Default::default(), | ||||
|             variants: Variant::all(), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn title(&self) -> String { | ||||
|         String::from("Scrollable - Iced") | ||||
|     } | ||||
| 
 | ||||
|     fn update(&mut self, message: Message) { | ||||
|         match message { | ||||
|             Message::ThemeChanged(theme) => self.theme = theme, | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     fn view(&mut self) -> Element<Message> { | ||||
|         let ScrollableDemo { | ||||
|             theme, variants, .. | ||||
|         } = self; | ||||
| 
 | ||||
|         let choose_theme = style::Theme::ALL.iter().fold( | ||||
|             Column::new().spacing(10).push(Text::new("Choose a theme:")), | ||||
|             |column, option| { | ||||
|                 column.push( | ||||
|                     Radio::new( | ||||
|                         *option, | ||||
|                         &format!("{:?}", option), | ||||
|                         Some(*theme), | ||||
|                         Message::ThemeChanged, | ||||
|                     ) | ||||
|                     .style(*theme), | ||||
|                 ) | ||||
|             }, | ||||
|         ); | ||||
| 
 | ||||
|         let scrollable_row = Row::with_children( | ||||
|             variants | ||||
|                 .iter_mut() | ||||
|                 .map(|variant| { | ||||
|                     let mut scrollable = Scrollable::new(&mut variant.state) | ||||
|                         .padding(10) | ||||
|                         .width(Length::Fill) | ||||
|                         .height(Length::Fill) | ||||
|                         .style(*theme) | ||||
|                         .push(Text::new(variant.title)); | ||||
| 
 | ||||
|                     if let Some(scrollbar_width) = variant.scrollbar_width { | ||||
|                         scrollable = scrollable | ||||
|                             .scrollbar_width(scrollbar_width) | ||||
|                             .push(Text::new(format!( | ||||
|                                 "scrollbar_width: {:?}", | ||||
|                                 scrollbar_width | ||||
|                             ))); | ||||
|                     } | ||||
| 
 | ||||
|                     if let Some(scrollbar_margin) = variant.scrollbar_margin { | ||||
|                         scrollable = scrollable | ||||
|                             .scrollbar_margin(scrollbar_margin) | ||||
|                             .push(Text::new(format!( | ||||
|                                 "scrollbar_margin: {:?}", | ||||
|                                 scrollbar_margin | ||||
|                             ))); | ||||
|                     } | ||||
| 
 | ||||
|                     if let Some(scroller_width) = variant.scroller_width { | ||||
|                         scrollable = scrollable | ||||
|                             .scroller_width(scroller_width) | ||||
|                             .push(Text::new(format!( | ||||
|                                 "scroller_width: {:?}", | ||||
|                                 scroller_width | ||||
|                             ))); | ||||
|                     } | ||||
| 
 | ||||
|                     scrollable = scrollable | ||||
|                         .push(Space::with_height(Length::Units(100))) | ||||
|                         .push(Text::new( | ||||
|                             "Some content that should wrap within the \ | ||||
|                             scrollable. Let's output a lot of short words, so \ | ||||
|                             that we'll make sure to see how wrapping works \ | ||||
|                             with these scrollbars.",
 | ||||
|                         )) | ||||
|                         .push(Space::with_height(Length::Units(1200))) | ||||
|                         .push(Text::new("Middle")) | ||||
|                         .push(Space::with_height(Length::Units(1200))) | ||||
|                         .push(Text::new("The End.")); | ||||
| 
 | ||||
|                     Container::new(scrollable) | ||||
|                         .width(Length::Fill) | ||||
|                         .height(Length::Fill) | ||||
|                         .style(*theme) | ||||
|                         .into() | ||||
|                 }) | ||||
|                 .collect(), | ||||
|         ) | ||||
|         .spacing(20) | ||||
|         .width(Length::Fill) | ||||
|         .height(Length::Fill); | ||||
| 
 | ||||
|         let content = Column::new() | ||||
|             .spacing(20) | ||||
|             .padding(20) | ||||
|             .push(choose_theme) | ||||
|             .push(Rule::horizontal(20).style(self.theme)) | ||||
|             .push(scrollable_row); | ||||
| 
 | ||||
|         Container::new(content) | ||||
|             .width(Length::Fill) | ||||
|             .height(Length::Fill) | ||||
|             .center_x() | ||||
|             .center_y() | ||||
|             .style(self.theme) | ||||
|             .into() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /// A version of a scrollable
 | ||||
| struct Variant { | ||||
|     title: &'static str, | ||||
|     state: scrollable::State, | ||||
|     scrollbar_width: Option<u16>, | ||||
|     scrollbar_margin: Option<u16>, | ||||
|     scroller_width: Option<u16>, | ||||
| } | ||||
| 
 | ||||
| impl Variant { | ||||
|     pub fn all() -> Vec<Self> { | ||||
|         vec![ | ||||
|             Self { | ||||
|                 title: "Default Scrollbar", | ||||
|                 state: scrollable::State::new(), | ||||
|                 scrollbar_width: None, | ||||
|                 scrollbar_margin: None, | ||||
|                 scroller_width: None, | ||||
|             }, | ||||
|             Self { | ||||
|                 title: "Slimmed & Margin", | ||||
|                 state: scrollable::State::new(), | ||||
|                 scrollbar_width: Some(4), | ||||
|                 scrollbar_margin: Some(3), | ||||
|                 scroller_width: Some(4), | ||||
|             }, | ||||
|             Self { | ||||
|                 title: "Wide Scroller", | ||||
|                 state: scrollable::State::new(), | ||||
|                 scrollbar_width: Some(4), | ||||
|                 scrollbar_margin: None, | ||||
|                 scroller_width: Some(10), | ||||
|             }, | ||||
|             Self { | ||||
|                 title: "Narrow Scroller", | ||||
|                 state: scrollable::State::new(), | ||||
|                 scrollbar_width: Some(10), | ||||
|                 scrollbar_margin: None, | ||||
|                 scroller_width: Some(4), | ||||
|             }, | ||||
|         ] | ||||
|     } | ||||
| } | ||||
							
								
								
									
										190
									
								
								examples/scrollable/src/style.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										190
									
								
								examples/scrollable/src/style.rs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,190 @@ | ||||
| use iced::{container, radio, rule, scrollable}; | ||||
| 
 | ||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq)] | ||||
| pub enum Theme { | ||||
|     Light, | ||||
|     Dark, | ||||
| } | ||||
| 
 | ||||
| impl Theme { | ||||
|     pub const ALL: [Theme; 2] = [Theme::Light, Theme::Dark]; | ||||
| } | ||||
| 
 | ||||
| impl Default for Theme { | ||||
|     fn default() -> Theme { | ||||
|         Theme::Light | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Theme> for Box<dyn container::StyleSheet> { | ||||
|     fn from(theme: Theme) -> Self { | ||||
|         match theme { | ||||
|             Theme::Light => Default::default(), | ||||
|             Theme::Dark => dark::Container.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Theme> for Box<dyn radio::StyleSheet> { | ||||
|     fn from(theme: Theme) -> Self { | ||||
|         match theme { | ||||
|             Theme::Light => Default::default(), | ||||
|             Theme::Dark => dark::Radio.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Theme> for Box<dyn scrollable::StyleSheet> { | ||||
|     fn from(theme: Theme) -> Self { | ||||
|         match theme { | ||||
|             Theme::Light => Default::default(), | ||||
|             Theme::Dark => dark::Scrollable.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| impl From<Theme> for Box<dyn rule::StyleSheet> { | ||||
|     fn from(theme: Theme) -> Self { | ||||
|         match theme { | ||||
|             Theme::Light => Default::default(), | ||||
|             Theme::Dark => dark::Rule.into(), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| mod dark { | ||||
|     use iced::{container, radio, rule, scrollable, Color}; | ||||
| 
 | ||||
|     const BACKGROUND: Color = Color::from_rgb( | ||||
|         0x36 as f32 / 255.0, | ||||
|         0x39 as f32 / 255.0, | ||||
|         0x3F as f32 / 255.0, | ||||
|     ); | ||||
| 
 | ||||
|     const SURFACE: Color = Color::from_rgb( | ||||
|         0x40 as f32 / 255.0, | ||||
|         0x44 as f32 / 255.0, | ||||
|         0x4B as f32 / 255.0, | ||||
|     ); | ||||
| 
 | ||||
|     const ACCENT: Color = Color::from_rgb( | ||||
|         0x6F as f32 / 255.0, | ||||
|         0xFF as f32 / 255.0, | ||||
|         0xE9 as f32 / 255.0, | ||||
|     ); | ||||
| 
 | ||||
|     const ACTIVE: Color = Color::from_rgb( | ||||
|         0x72 as f32 / 255.0, | ||||
|         0x89 as f32 / 255.0, | ||||
|         0xDA as f32 / 255.0, | ||||
|     ); | ||||
| 
 | ||||
|     const SCROLLBAR: Color = Color::from_rgb( | ||||
|         0x2E as f32 / 255.0, | ||||
|         0x33 as f32 / 255.0, | ||||
|         0x38 as f32 / 255.0, | ||||
|     ); | ||||
| 
 | ||||
|     const SCROLLER: Color = Color::from_rgb( | ||||
|         0x20 as f32 / 255.0, | ||||
|         0x22 as f32 / 255.0, | ||||
|         0x25 as f32 / 255.0, | ||||
|     ); | ||||
| 
 | ||||
|     pub struct Container; | ||||
| 
 | ||||
|     impl container::StyleSheet for Container { | ||||
|         fn style(&self) -> container::Style { | ||||
|             container::Style { | ||||
|                 background: Color { | ||||
|                     a: 0.99, | ||||
|                     ..BACKGROUND | ||||
|                 } | ||||
|                 .into(), | ||||
|                 text_color: Color::WHITE.into(), | ||||
|                 ..container::Style::default() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub struct Radio; | ||||
| 
 | ||||
|     impl radio::StyleSheet for Radio { | ||||
|         fn active(&self) -> radio::Style { | ||||
|             radio::Style { | ||||
|                 background: SURFACE.into(), | ||||
|                 dot_color: ACTIVE, | ||||
|                 border_width: 1, | ||||
|                 border_color: ACTIVE, | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         fn hovered(&self) -> radio::Style { | ||||
|             radio::Style { | ||||
|                 background: Color { a: 0.5, ..SURFACE }.into(), | ||||
|                 ..self.active() | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub struct Scrollable; | ||||
| 
 | ||||
|     impl scrollable::StyleSheet for Scrollable { | ||||
|         fn active(&self) -> scrollable::Scrollbar { | ||||
|             scrollable::Scrollbar { | ||||
|                 background: Color { | ||||
|                     a: 0.8, | ||||
|                     ..SCROLLBAR | ||||
|                 } | ||||
|                 .into(), | ||||
|                 border_radius: 2, | ||||
|                 border_width: 0, | ||||
|                 border_color: Color::TRANSPARENT, | ||||
|                 scroller: scrollable::Scroller { | ||||
|                     color: Color { a: 0.7, ..SCROLLER }, | ||||
|                     border_radius: 2, | ||||
|                     border_width: 0, | ||||
|                     border_color: Color::TRANSPARENT, | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         fn hovered(&self) -> scrollable::Scrollbar { | ||||
|             let active = self.active(); | ||||
| 
 | ||||
|             scrollable::Scrollbar { | ||||
|                 background: SCROLLBAR.into(), | ||||
|                 scroller: scrollable::Scroller { | ||||
|                     color: SCROLLER, | ||||
|                     ..active.scroller | ||||
|                 }, | ||||
|                 ..active | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         fn dragging(&self) -> scrollable::Scrollbar { | ||||
|             let hovered = self.hovered(); | ||||
| 
 | ||||
|             scrollable::Scrollbar { | ||||
|                 scroller: scrollable::Scroller { | ||||
|                     color: ACCENT, | ||||
|                     ..hovered.scroller | ||||
|                 }, | ||||
|                 ..hovered | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub struct Rule; | ||||
| 
 | ||||
|     impl rule::StyleSheet for Rule { | ||||
|         fn style(&self) -> rule::Style { | ||||
|             rule::Style { | ||||
|                 color: SURFACE, | ||||
|                 width: 2, | ||||
|                 radius: 1, | ||||
|                 fill_mode: rule::FillMode::Percent(30.0), | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -15,9 +15,6 @@ pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; | ||||
| pub type Scrollable<'a, Message, Backend> = | ||||
|     iced_native::Scrollable<'a, Message, Renderer<Backend>>; | ||||
| 
 | ||||
| const SCROLLBAR_WIDTH: u16 = 10; | ||||
| const SCROLLBAR_MARGIN: u16 = 2; | ||||
| 
 | ||||
| impl<B> scrollable::Renderer for Renderer<B> | ||||
| where | ||||
|     B: Backend, | ||||
| @ -29,29 +26,45 @@ where | ||||
|         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(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), | ||||
|                     - f32::from(outer_width / 2 + scrollbar_width / 2), | ||||
|                 y: bounds.y, | ||||
|                 width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), | ||||
|                 width: scrollbar_width as f32, | ||||
|                 height: bounds.height, | ||||
|             }; | ||||
| 
 | ||||
|             let ratio = bounds.height / content_bounds.height; | ||||
|             let scrollbar_height = bounds.height * ratio; | ||||
|             let scroller_height = bounds.height * ratio; | ||||
|             let y_offset = offset as f32 * ratio; | ||||
| 
 | ||||
|             let scroller_bounds = Rectangle { | ||||
|                 x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), | ||||
|                 x: bounds.x + bounds.width | ||||
|                     - f32::from(outer_width / 2 + scroller_width / 2), | ||||
|                 y: scrollbar_bounds.y + y_offset, | ||||
|                 width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), | ||||
|                 height: scrollbar_height, | ||||
|                 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, | ||||
|                 }, | ||||
| @ -109,12 +122,7 @@ where | ||||
| 
 | ||||
|                 let scrollbar = if is_scrollbar_visible { | ||||
|                     Primitive::Quad { | ||||
|                         bounds: Rectangle { | ||||
|                             x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN), | ||||
|                             width: scrollbar.bounds.width | ||||
|                                 - f32::from(2 * SCROLLBAR_MARGIN), | ||||
|                             ..scrollbar.bounds | ||||
|                         }, | ||||
|                         bounds: scrollbar.bounds, | ||||
|                         background: style | ||||
|                             .background | ||||
|                             .unwrap_or(Background::Color(Color::TRANSPARENT)), | ||||
|  | ||||
| @ -89,6 +89,9 @@ impl scrollable::Renderer for Null { | ||||
|         _bounds: Rectangle, | ||||
|         _content_bounds: Rectangle, | ||||
|         _offset: u32, | ||||
|         _scrollbar_width: u16, | ||||
|         _scrollbar_margin: u16, | ||||
|         _scroller_width: u16, | ||||
|     ) -> Option<scrollable::Scrollbar> { | ||||
|         None | ||||
|     } | ||||
|  | ||||
| @ -13,6 +13,9 @@ pub struct Scrollable<'a, Message, Renderer: self::Renderer> { | ||||
|     state: &'a mut State, | ||||
|     height: Length, | ||||
|     max_height: u32, | ||||
|     scrollbar_width: u16, | ||||
|     scrollbar_margin: u16, | ||||
|     scroller_width: u16, | ||||
|     content: Column<'a, Message, Renderer>, | ||||
|     style: Renderer::Style, | ||||
| } | ||||
| @ -27,6 +30,9 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { | ||||
|             state, | ||||
|             height: Length::Shrink, | ||||
|             max_height: u32::MAX, | ||||
|             scrollbar_width: 10, | ||||
|             scrollbar_margin: 0, | ||||
|             scroller_width: 10, | ||||
|             content: Column::new(), | ||||
|             style: Renderer::Style::default(), | ||||
|         } | ||||
| @ -90,6 +96,32 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Sets the scrollbar width of the [`Scrollable`] .
 | ||||
|     /// Silently enforces a minimum value of 1.
 | ||||
|     ///
 | ||||
|     /// [`Scrollable`]: struct.Scrollable.html
 | ||||
|     pub fn scrollbar_width(mut self, scrollbar_width: u16) -> Self { | ||||
|         self.scrollbar_width = scrollbar_width.max(1); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Sets the scrollbar margin of the [`Scrollable`] .
 | ||||
|     ///
 | ||||
|     /// [`Scrollable`]: struct.Scrollable.html
 | ||||
|     pub fn scrollbar_margin(mut self, scrollbar_margin: u16) -> Self { | ||||
|         self.scrollbar_margin = scrollbar_margin; | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Sets the scroller width of the [`Scrollable`] .
 | ||||
|     /// Silently enforces a minimum value of 1.
 | ||||
|     ///
 | ||||
|     /// [`Scrollable`]: struct.Scrollable.html
 | ||||
|     pub fn scroller_width(mut self, scroller_width: u16) -> Self { | ||||
|         self.scroller_width = scroller_width.max(1); | ||||
|         self | ||||
|     } | ||||
| 
 | ||||
|     /// Sets the style of the [`Scrollable`] .
 | ||||
|     ///
 | ||||
|     /// [`Scrollable`]: struct.Scrollable.html
 | ||||
| @ -178,7 +210,14 @@ where | ||||
|         } | ||||
| 
 | ||||
|         let offset = self.state.offset(bounds, content_bounds); | ||||
|         let scrollbar = renderer.scrollbar(bounds, content_bounds, offset); | ||||
|         let scrollbar = renderer.scrollbar( | ||||
|             bounds, | ||||
|             content_bounds, | ||||
|             offset, | ||||
|             self.scrollbar_width, | ||||
|             self.scrollbar_margin, | ||||
|             self.scroller_width, | ||||
|         ); | ||||
|         let is_mouse_over_scrollbar = scrollbar | ||||
|             .as_ref() | ||||
|             .map(|scrollbar| scrollbar.is_mouse_over(cursor_position)) | ||||
| @ -269,7 +308,14 @@ 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); | ||||
|         let scrollbar = renderer.scrollbar( | ||||
|             bounds, | ||||
|             content_bounds, | ||||
|             offset, | ||||
|             self.scrollbar_width, | ||||
|             self.scrollbar_margin, | ||||
|             self.scroller_width, | ||||
|         ); | ||||
| 
 | ||||
|         let is_mouse_over = bounds.contains(cursor_position); | ||||
|         let is_mouse_over_scrollbar = scrollbar | ||||
| @ -413,11 +459,23 @@ impl State { | ||||
| /// [`Scrollable`]: struct.Scrollable.html
 | ||||
| #[derive(Debug)] | ||||
| pub struct Scrollbar { | ||||
|     /// The outer bounds of the scrollable, including the [`Scrollbar`] and
 | ||||
|     /// [`Scroller`].
 | ||||
|     ///
 | ||||
|     /// [`Scrollbar`]: struct.Scrollbar.html
 | ||||
|     /// [`Scroller`]: struct.Scroller.html
 | ||||
|     pub outer_bounds: Rectangle, | ||||
| 
 | ||||
|     /// The bounds of the [`Scrollbar`].
 | ||||
|     ///
 | ||||
|     /// [`Scrollbar`]: struct.Scrollbar.html
 | ||||
|     pub bounds: Rectangle, | ||||
| 
 | ||||
|     /// The margin within the [`Scrollbar`].
 | ||||
|     ///
 | ||||
|     /// [`Scrollbar`]: struct.Scrollbar.html
 | ||||
|     pub margin: u16, | ||||
| 
 | ||||
|     /// The bounds of the [`Scroller`].
 | ||||
|     ///
 | ||||
|     /// [`Scroller`]: struct.Scroller.html
 | ||||
| @ -426,11 +484,11 @@ pub struct Scrollbar { | ||||
| 
 | ||||
| impl Scrollbar { | ||||
|     fn is_mouse_over(&self, cursor_position: Point) -> bool { | ||||
|         self.bounds.contains(cursor_position) | ||||
|         self.outer_bounds.contains(cursor_position) | ||||
|     } | ||||
| 
 | ||||
|     fn grab_scroller(&self, cursor_position: Point) -> Option<f32> { | ||||
|         if self.bounds.contains(cursor_position) { | ||||
|         if self.outer_bounds.contains(cursor_position) { | ||||
|             Some(if self.scroller.bounds.contains(cursor_position) { | ||||
|                 (cursor_position.y - self.scroller.bounds.y) | ||||
|                     / self.scroller.bounds.height | ||||
| @ -486,6 +544,9 @@ pub trait Renderer: column::Renderer + Sized { | ||||
|         bounds: Rectangle, | ||||
|         content_bounds: Rectangle, | ||||
|         offset: u32, | ||||
|         scrollbar_width: u16, | ||||
|         scrollbar_margin: u16, | ||||
|         scroller_width: u16, | ||||
|     ) -> Option<Scrollbar>; | ||||
| 
 | ||||
|     /// Draws the [`Scrollable`].
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user