From 09e67c5c2701e7eeeb0bdb924966f8442c698b6a Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 22 Oct 2020 14:07:07 -0500 Subject: Replace hard-coded params with struct members --- graphics/src/widget/scrollable.rs | 19 +++++++------ native/src/renderer/null.rs | 3 ++ native/src/widget/scrollable.rs | 58 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index b149db0a..0a3191fa 100644 --- a/graphics/src/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -15,9 +15,6 @@ pub use iced_style::scrollable::{Scrollbar, Scroller, StyleSheet}; pub type Scrollable<'a, Message, Backend> = iced_native::Scrollable<'a, Message, Renderer>; -const SCROLLBAR_WIDTH: u16 = 10; -const SCROLLBAR_MARGIN: u16 = 2; - impl scrollable::Renderer for Renderer where B: Backend, @@ -29,13 +26,16 @@ where bounds: Rectangle, content_bounds: Rectangle, offset: u32, + scrollbar_width: u16, + scrollbar_margin: u16, + _scroller_width: u16, ) -> Option { if content_bounds.height > bounds.height { let scrollbar_bounds = Rectangle { x: bounds.x + bounds.width - - f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + - f32::from(scrollbar_width + 2 * scrollbar_margin), y: bounds.y, - width: f32::from(SCROLLBAR_WIDTH + 2 * SCROLLBAR_MARGIN), + width: f32::from(scrollbar_width + 2 * scrollbar_margin), height: bounds.height, }; @@ -44,14 +44,15 @@ where let y_offset = offset as f32 * ratio; let scroller_bounds = Rectangle { - x: scrollbar_bounds.x + f32::from(SCROLLBAR_MARGIN), + x: scrollbar_bounds.x + f32::from(scrollbar_margin), y: scrollbar_bounds.y + y_offset, - width: scrollbar_bounds.width - f32::from(2 * SCROLLBAR_MARGIN), + width: scrollbar_bounds.width - f32::from(2 * scrollbar_margin), height: scrollbar_height, }; Some(scrollable::Scrollbar { bounds: scrollbar_bounds, + margin: scrollbar_margin, scroller: scrollable::Scroller { bounds: scroller_bounds, }, @@ -110,9 +111,9 @@ where let scrollbar = if is_scrollbar_visible { Primitive::Quad { bounds: Rectangle { - x: scrollbar.bounds.x + f32::from(SCROLLBAR_MARGIN), + x: scrollbar.bounds.x + f32::from(scrollbar.margin), width: scrollbar.bounds.width - - f32::from(2 * SCROLLBAR_MARGIN), + - f32::from(2 * scrollbar.margin), ..scrollbar.bounds }, background: style diff --git a/native/src/renderer/null.rs b/native/src/renderer/null.rs index 2aee0da1..b06b0e28 100644 --- a/native/src/renderer/null.rs +++ b/native/src/renderer/null.rs @@ -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 { None } diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 75e97027..1cbc2bbf 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -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: 2, + 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 @@ -418,6 +464,11 @@ pub struct 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 @@ -486,6 +537,9 @@ pub trait Renderer: column::Renderer + Sized { bounds: Rectangle, content_bounds: Rectangle, offset: u32, + scrollbar_width: u16, + scrollbar_margin: u16, + scroller_width: u16, ) -> Option; /// Draws the [`Scrollable`]. -- cgit From f05578c8f88b38cb538657e40c2feb725e72c7d8 Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 22 Oct 2020 16:05:44 -0500 Subject: Update scrollbar logic and introduce outer_bounds --- graphics/src/widget/scrollable.rs | 33 ++++++++++++++++++++------------- native/src/widget/scrollable.rs | 13 ++++++++++--- 2 files changed, 30 insertions(+), 16 deletions(-) diff --git a/graphics/src/widget/scrollable.rs b/graphics/src/widget/scrollable.rs index 0a3191fa..fed79c18 100644 --- a/graphics/src/widget/scrollable.rs +++ b/graphics/src/widget/scrollable.rs @@ -28,29 +28,41 @@ where offset: u32, scrollbar_width: u16, scrollbar_margin: u16, - _scroller_width: u16, + scroller_width: u16, ) -> Option { 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 { @@ -110,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)), diff --git a/native/src/widget/scrollable.rs b/native/src/widget/scrollable.rs index 1cbc2bbf..cb181899 100644 --- a/native/src/widget/scrollable.rs +++ b/native/src/widget/scrollable.rs @@ -31,7 +31,7 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> { height: Length::Shrink, max_height: u32::MAX, scrollbar_width: 10, - scrollbar_margin: 2, + scrollbar_margin: 0, scroller_width: 10, content: Column::new(), style: Renderer::Style::default(), @@ -459,6 +459,13 @@ 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 @@ -477,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 { - 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 -- cgit From e43a46952ad3b76f76a7050430ec26477c795c4f Mon Sep 17 00:00:00 2001 From: Clark Moody Date: Thu, 22 Oct 2020 16:10:10 -0500 Subject: Add scrollable example program --- Cargo.toml | 1 + examples/README.md | 1 + examples/scrollable/Cargo.toml | 9 + examples/scrollable/README.md | 15 ++ examples/scrollable/screenshot.png | Bin 0 -> 238486 bytes examples/scrollable/src/main.rs | 365 +++++++++++++++++++++++++++++++++++++ 6 files changed, 391 insertions(+) create mode 100644 examples/scrollable/Cargo.toml create mode 100644 examples/scrollable/README.md create mode 100644 examples/scrollable/screenshot.png create mode 100644 examples/scrollable/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index d97707ab..e201a4b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -67,6 +67,7 @@ members = [ "examples/pick_list", "examples/pokedex", "examples/progress_bar", + "examples/scrollable", "examples/solar_system", "examples/stopwatch", "examples/styling", diff --git a/examples/README.md b/examples/README.md index 34a916a1..32ccf724 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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. diff --git a/examples/scrollable/Cargo.toml b/examples/scrollable/Cargo.toml new file mode 100644 index 00000000..12753fb6 --- /dev/null +++ b/examples/scrollable/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "scrollable" +version = "0.1.0" +authors = ["Clark Moody "] +edition = "2018" +publish = false + +[dependencies] +iced = { path = "../.." } diff --git a/examples/scrollable/README.md b/examples/scrollable/README.md new file mode 100644 index 00000000..ed0e31b5 --- /dev/null +++ b/examples/scrollable/README.md @@ -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. + + + +You can run it with `cargo run`: +``` +cargo run --package scrollable +``` diff --git a/examples/scrollable/screenshot.png b/examples/scrollable/screenshot.png new file mode 100644 index 00000000..f2add949 Binary files /dev/null and b/examples/scrollable/screenshot.png differ diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs new file mode 100644 index 00000000..da38cd7d --- /dev/null +++ b/examples/scrollable/src/main.rs @@ -0,0 +1,365 @@ +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, + scroll: Vec, + config: Vec, +} + +#[derive(Debug, Clone)] +enum Message { + ThemeChanged(style::Theme), +} + +/// Contains configuration for a single scrollbar +struct Config { + top_content: String, + scrollbar_width: Option, + scrollbar_margin: Option, + scroller_width: Option, +} + +fn get_configs() -> Vec { + vec![ + Config { + top_content: "Default Scrollbar".into(), + scrollbar_width: None, + scrollbar_margin: None, + scroller_width: None, + }, + Config { + top_content: "Slimmed & Margin".into(), + scrollbar_width: Some(4), + scrollbar_margin: Some(3), + scroller_width: Some(4), + }, + Config { + top_content: "Wide Scroller".into(), + scrollbar_width: Some(4), + scrollbar_margin: None, + scroller_width: Some(10), + }, + Config { + top_content: "Narrow Scroller".into(), + scrollbar_width: Some(10), + scrollbar_margin: None, + scroller_width: Some(4), + }, + ] +} + +impl Sandbox for ScrollableDemo { + type Message = Message; + + fn new() -> Self { + let config = get_configs(); + ScrollableDemo { + theme: Default::default(), + scroll: vec![scrollable::State::default(); config.len()], + config, + } + } + + 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 { + let choose_theme = style::Theme::ALL.iter().fold( + Column::new().spacing(10).push(Text::new("Choose a theme:")), + |column, theme| { + column.push( + Radio::new( + *theme, + &format!("{:?}", theme), + Some(self.theme), + Message::ThemeChanged, + ) + .style(self.theme), + ) + }, + ); + + let ScrollableDemo { scroll, theme, .. } = self; + + let scrollable_row = Row::with_children( + scroll + .iter_mut() + .zip(self.config.iter()) + .map(|(state, config)| { + let mut scrollable = Scrollable::new(state) + .padding(10) + .width(Length::Fill) + .height(Length::Fill) + .style(*theme) + .push(Text::new(config.top_content.clone())); + + if let Some(scrollbar_width) = config.scrollbar_width { + scrollable = scrollable + .scrollbar_width(scrollbar_width) + .push(Text::new(format!( + "scrollbar_width: {:?}", + scrollbar_width + ))); + } + if let Some(scrollbar_margin) = config.scrollbar_margin { + scrollable = scrollable + .scrollbar_margin(scrollbar_margin) + .push(Text::new(format!( + "scrollbar_margin: {:?}", + scrollbar_margin + ))); + } + if let Some(scroller_width) = config.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() + } +} + +mod style { + 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 for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Container.into(), + } + } + } + + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Radio.into(), + } + } + } + + impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Scrollable.into(), + } + } + } + + impl From for Box { + 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( + 0x0f as f32 / 255.0, + 0x14 as f32 / 255.0, + 0x19 as f32 / 255.0, + ); + + const LIGHTER_BACKGROUND: Color = Color::from_rgb( + 0x14 as f32 / 255.0, + 0x19 as f32 / 255.0, + 0x1f as f32 / 255.0, + ); + + const YELLOW: Color = Color::from_rgb( + 0xff as f32 / 255.0, + 0xb4 as f32 / 255.0, + 0x54 as f32 / 255.0, + ); + + const CYAN: Color = Color::from_rgb( + 0x39 as f32 / 255.0, + 0xaf as f32 / 255.0, + 0xd7 as f32 / 255.0, + ); + + const CYAN_LIGHT: Color = Color::from_rgb( + 0x5d as f32 / 255.0, + 0xb7 as f32 / 255.0, + 0xd5 as f32 / 255.0, + ); + + const ORANGE: Color = Color::from_rgb( + 0xff as f32 / 255.0, + 0x77 as f32 / 255.0, + 0x33 as f32 / 255.0, + ); + + const ORANGE_DARK: Color = Color::from_rgb( + 0xe6 as f32 / 255.0, + 0x5b as f32 / 255.0, + 0x16 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: BACKGROUND.into(), + dot_color: CYAN, + border_width: 1, + border_color: CYAN, + } + } + + fn hovered(&self) -> radio::Style { + radio::Style { + background: LIGHTER_BACKGROUND.into(), + ..self.active() + } + } + } + + pub struct Scrollable; + + impl scrollable::StyleSheet for Scrollable { + fn active(&self) -> scrollable::Scrollbar { + scrollable::Scrollbar { + background: CYAN.into(), + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + scroller: scrollable::Scroller { + color: YELLOW, + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + } + } + + fn hovered(&self) -> scrollable::Scrollbar { + let active = self.active(); + + scrollable::Scrollbar { + background: CYAN_LIGHT.into(), + scroller: scrollable::Scroller { + color: ORANGE, + ..active.scroller + }, + ..active + } + } + + fn dragging(&self) -> scrollable::Scrollbar { + let hovered = self.hovered(); + + scrollable::Scrollbar { + scroller: scrollable::Scroller { + color: ORANGE_DARK, + ..hovered.scroller + }, + ..hovered + } + } + } + + pub struct Rule; + + impl rule::StyleSheet for Rule { + fn style(&self) -> rule::Style { + rule::Style { + color: CYAN, + width: 2, + radius: 1, + fill_mode: rule::FillMode::Percent(15.0), + } + } + } + } +} -- cgit From 7f66345d5a20c847db18893d1b8717cf1669d5b1 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 24 Oct 2020 10:00:49 +0200 Subject: Improve minor details in `scrollable` example - Rename `Config` to `Variant` - Include `State` in `Variant` to avoid `zip` - Break long string literal - Split `style` module into its own file --- examples/scrollable/src/main.rs | 317 +++++++++------------------------------ examples/scrollable/src/style.rs | 192 ++++++++++++++++++++++++ 2 files changed, 260 insertions(+), 249 deletions(-) create mode 100644 examples/scrollable/src/style.rs diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs index da38cd7d..8dd2e20c 100644 --- a/examples/scrollable/src/main.rs +++ b/examples/scrollable/src/main.rs @@ -1,3 +1,5 @@ +mod style; + use iced::{ scrollable, Column, Container, Element, Length, Radio, Row, Rule, Sandbox, Scrollable, Settings, Space, Text, @@ -9,8 +11,7 @@ pub fn main() -> iced::Result { struct ScrollableDemo { theme: style::Theme, - scroll: Vec, - config: Vec, + variants: Vec, } #[derive(Debug, Clone)] @@ -18,52 +19,13 @@ enum Message { ThemeChanged(style::Theme), } -/// Contains configuration for a single scrollbar -struct Config { - top_content: String, - scrollbar_width: Option, - scrollbar_margin: Option, - scroller_width: Option, -} - -fn get_configs() -> Vec { - vec![ - Config { - top_content: "Default Scrollbar".into(), - scrollbar_width: None, - scrollbar_margin: None, - scroller_width: None, - }, - Config { - top_content: "Slimmed & Margin".into(), - scrollbar_width: Some(4), - scrollbar_margin: Some(3), - scroller_width: Some(4), - }, - Config { - top_content: "Wide Scroller".into(), - scrollbar_width: Some(4), - scrollbar_margin: None, - scroller_width: Some(10), - }, - Config { - top_content: "Narrow Scroller".into(), - scrollbar_width: Some(10), - scrollbar_margin: None, - scroller_width: Some(4), - }, - ] -} - impl Sandbox for ScrollableDemo { type Message = Message; fn new() -> Self { - let config = get_configs(); ScrollableDemo { theme: Default::default(), - scroll: vec![scrollable::State::default(); config.len()], - config, + variants: Variant::all(), } } @@ -78,36 +40,37 @@ impl Sandbox for ScrollableDemo { } fn view(&mut self) -> Element { + let ScrollableDemo { + theme, variants, .. + } = self; + let choose_theme = style::Theme::ALL.iter().fold( Column::new().spacing(10).push(Text::new("Choose a theme:")), - |column, theme| { + |column, option| { column.push( Radio::new( - *theme, - &format!("{:?}", theme), - Some(self.theme), + *option, + &format!("{:?}", option), + Some(*theme), Message::ThemeChanged, ) - .style(self.theme), + .style(*theme), ) }, ); - let ScrollableDemo { scroll, theme, .. } = self; - let scrollable_row = Row::with_children( - scroll + variants .iter_mut() - .zip(self.config.iter()) - .map(|(state, config)| { - let mut scrollable = Scrollable::new(state) + .map(|variant| { + let mut scrollable = Scrollable::new(&mut variant.state) .padding(10) .width(Length::Fill) .height(Length::Fill) .style(*theme) - .push(Text::new(config.top_content.clone())); + .push(Text::new(variant.title)); - if let Some(scrollbar_width) = config.scrollbar_width { + if let Some(scrollbar_width) = variant.scrollbar_width { scrollable = scrollable .scrollbar_width(scrollbar_width) .push(Text::new(format!( @@ -115,7 +78,8 @@ impl Sandbox for ScrollableDemo { scrollbar_width ))); } - if let Some(scrollbar_margin) = config.scrollbar_margin { + + if let Some(scrollbar_margin) = variant.scrollbar_margin { scrollable = scrollable .scrollbar_margin(scrollbar_margin) .push(Text::new(format!( @@ -123,7 +87,8 @@ impl Sandbox for ScrollableDemo { scrollbar_margin ))); } - if let Some(scroller_width) = config.scroller_width { + + if let Some(scroller_width) = variant.scroller_width { scrollable = scrollable .scroller_width(scroller_width) .push(Text::new(format!( @@ -134,7 +99,12 @@ impl Sandbox for ScrollableDemo { 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(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))) @@ -169,197 +139,46 @@ impl Sandbox for ScrollableDemo { } } -mod style { - 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 for Box { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Container.into(), - } - } - } - - impl From for Box { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Radio.into(), - } - } - } - - impl From for Box { - fn from(theme: Theme) -> Self { - match theme { - Theme::Light => Default::default(), - Theme::Dark => dark::Scrollable.into(), - } - } - } - - impl From for Box { - 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( - 0x0f as f32 / 255.0, - 0x14 as f32 / 255.0, - 0x19 as f32 / 255.0, - ); - - const LIGHTER_BACKGROUND: Color = Color::from_rgb( - 0x14 as f32 / 255.0, - 0x19 as f32 / 255.0, - 0x1f as f32 / 255.0, - ); - - const YELLOW: Color = Color::from_rgb( - 0xff as f32 / 255.0, - 0xb4 as f32 / 255.0, - 0x54 as f32 / 255.0, - ); - - const CYAN: Color = Color::from_rgb( - 0x39 as f32 / 255.0, - 0xaf as f32 / 255.0, - 0xd7 as f32 / 255.0, - ); - - const CYAN_LIGHT: Color = Color::from_rgb( - 0x5d as f32 / 255.0, - 0xb7 as f32 / 255.0, - 0xd5 as f32 / 255.0, - ); - - const ORANGE: Color = Color::from_rgb( - 0xff as f32 / 255.0, - 0x77 as f32 / 255.0, - 0x33 as f32 / 255.0, - ); - - const ORANGE_DARK: Color = Color::from_rgb( - 0xe6 as f32 / 255.0, - 0x5b as f32 / 255.0, - 0x16 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: BACKGROUND.into(), - dot_color: CYAN, - border_width: 1, - border_color: CYAN, - } - } - - fn hovered(&self) -> radio::Style { - radio::Style { - background: LIGHTER_BACKGROUND.into(), - ..self.active() - } - } - } - - pub struct Scrollable; - - impl scrollable::StyleSheet for Scrollable { - fn active(&self) -> scrollable::Scrollbar { - scrollable::Scrollbar { - background: CYAN.into(), - border_radius: 2, - border_width: 0, - border_color: Color::TRANSPARENT, - scroller: scrollable::Scroller { - color: YELLOW, - border_radius: 2, - border_width: 0, - border_color: Color::TRANSPARENT, - }, - } - } - - fn hovered(&self) -> scrollable::Scrollbar { - let active = self.active(); - - scrollable::Scrollbar { - background: CYAN_LIGHT.into(), - scroller: scrollable::Scroller { - color: ORANGE, - ..active.scroller - }, - ..active - } - } - - fn dragging(&self) -> scrollable::Scrollbar { - let hovered = self.hovered(); - - scrollable::Scrollbar { - scroller: scrollable::Scroller { - color: ORANGE_DARK, - ..hovered.scroller - }, - ..hovered - } - } - } - - pub struct Rule; +/// A version of a scrollable +struct Variant { + title: &'static str, + state: scrollable::State, + scrollbar_width: Option, + scrollbar_margin: Option, + scroller_width: Option, +} - impl rule::StyleSheet for Rule { - fn style(&self) -> rule::Style { - rule::Style { - color: CYAN, - width: 2, - radius: 1, - fill_mode: rule::FillMode::Percent(15.0), - } - } - } +impl Variant { + pub fn all() -> Vec { + 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), + }, + ] } } diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs new file mode 100644 index 00000000..c6a33192 --- /dev/null +++ b/examples/scrollable/src/style.rs @@ -0,0 +1,192 @@ +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 for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Container.into(), + } + } +} + +impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Radio.into(), + } + } +} + +impl From for Box { + fn from(theme: Theme) -> Self { + match theme { + Theme::Light => Default::default(), + Theme::Dark => dark::Scrollable.into(), + } + } +} + +impl From for Box { + 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( + 0x0f as f32 / 255.0, + 0x14 as f32 / 255.0, + 0x19 as f32 / 255.0, + ); + + const LIGHTER_BACKGROUND: Color = Color::from_rgb( + 0x14 as f32 / 255.0, + 0x19 as f32 / 255.0, + 0x1f as f32 / 255.0, + ); + + const YELLOW: Color = Color::from_rgb( + 0xff as f32 / 255.0, + 0xb4 as f32 / 255.0, + 0x54 as f32 / 255.0, + ); + + const CYAN: Color = Color::from_rgb( + 0x39 as f32 / 255.0, + 0xaf as f32 / 255.0, + 0xd7 as f32 / 255.0, + ); + + const CYAN_LIGHT: Color = Color::from_rgb( + 0x5d as f32 / 255.0, + 0xb7 as f32 / 255.0, + 0xd5 as f32 / 255.0, + ); + + const ORANGE: Color = Color::from_rgb( + 0xff as f32 / 255.0, + 0x77 as f32 / 255.0, + 0x33 as f32 / 255.0, + ); + + const ORANGE_DARK: Color = Color::from_rgb( + 0xe6 as f32 / 255.0, + 0x5b as f32 / 255.0, + 0x16 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: BACKGROUND.into(), + dot_color: CYAN, + border_width: 1, + border_color: CYAN, + } + } + + fn hovered(&self) -> radio::Style { + radio::Style { + background: LIGHTER_BACKGROUND.into(), + ..self.active() + } + } + } + + pub struct Scrollable; + + impl scrollable::StyleSheet for Scrollable { + fn active(&self) -> scrollable::Scrollbar { + scrollable::Scrollbar { + background: CYAN.into(), + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + scroller: scrollable::Scroller { + color: YELLOW, + border_radius: 2, + border_width: 0, + border_color: Color::TRANSPARENT, + }, + } + } + + fn hovered(&self) -> scrollable::Scrollbar { + let active = self.active(); + + scrollable::Scrollbar { + background: CYAN_LIGHT.into(), + scroller: scrollable::Scroller { + color: ORANGE, + ..active.scroller + }, + ..active + } + } + + fn dragging(&self) -> scrollable::Scrollbar { + let hovered = self.hovered(); + + scrollable::Scrollbar { + scroller: scrollable::Scroller { + color: ORANGE_DARK, + ..hovered.scroller + }, + ..hovered + } + } + } + + pub struct Rule; + + impl rule::StyleSheet for Rule { + fn style(&self) -> rule::Style { + rule::Style { + color: CYAN, + width: 2, + radius: 1, + fill_mode: rule::FillMode::Percent(15.0), + } + } + } +} -- cgit From ed458d33d9b280df136bb098e9a2ca749055b9b6 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 24 Oct 2020 10:29:19 +0200 Subject: Reduce contrast of dark theme in `scrollable` example --- examples/scrollable/src/style.rs | 76 +++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 39 deletions(-) diff --git a/examples/scrollable/src/style.rs b/examples/scrollable/src/style.rs index c6a33192..24d711ac 100644 --- a/examples/scrollable/src/style.rs +++ b/examples/scrollable/src/style.rs @@ -56,45 +56,39 @@ mod dark { use iced::{container, radio, rule, scrollable, Color}; const BACKGROUND: Color = Color::from_rgb( - 0x0f as f32 / 255.0, - 0x14 as f32 / 255.0, - 0x19 as f32 / 255.0, - ); - - const LIGHTER_BACKGROUND: Color = Color::from_rgb( - 0x14 as f32 / 255.0, - 0x19 as f32 / 255.0, - 0x1f as f32 / 255.0, + 0x36 as f32 / 255.0, + 0x39 as f32 / 255.0, + 0x3F as f32 / 255.0, ); - const YELLOW: Color = Color::from_rgb( - 0xff as f32 / 255.0, - 0xb4 as f32 / 255.0, - 0x54 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 CYAN: Color = Color::from_rgb( - 0x39 as f32 / 255.0, - 0xaf as f32 / 255.0, - 0xd7 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 CYAN_LIGHT: Color = Color::from_rgb( - 0x5d as f32 / 255.0, - 0xb7 as f32 / 255.0, - 0xd5 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 ORANGE: Color = Color::from_rgb( - 0xff as f32 / 255.0, - 0x77 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 ORANGE_DARK: Color = Color::from_rgb( - 0xe6 as f32 / 255.0, - 0x5b as f32 / 255.0, - 0x16 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; @@ -118,16 +112,16 @@ mod dark { impl radio::StyleSheet for Radio { fn active(&self) -> radio::Style { radio::Style { - background: BACKGROUND.into(), - dot_color: CYAN, + background: SURFACE.into(), + dot_color: ACTIVE, border_width: 1, - border_color: CYAN, + border_color: ACTIVE, } } fn hovered(&self) -> radio::Style { radio::Style { - background: LIGHTER_BACKGROUND.into(), + background: Color { a: 0.5, ..SURFACE }.into(), ..self.active() } } @@ -138,12 +132,16 @@ mod dark { impl scrollable::StyleSheet for Scrollable { fn active(&self) -> scrollable::Scrollbar { scrollable::Scrollbar { - background: CYAN.into(), + background: Color { + a: 0.8, + ..SCROLLBAR + } + .into(), border_radius: 2, border_width: 0, border_color: Color::TRANSPARENT, scroller: scrollable::Scroller { - color: YELLOW, + color: Color { a: 0.7, ..SCROLLER }, border_radius: 2, border_width: 0, border_color: Color::TRANSPARENT, @@ -155,9 +153,9 @@ mod dark { let active = self.active(); scrollable::Scrollbar { - background: CYAN_LIGHT.into(), + background: SCROLLBAR.into(), scroller: scrollable::Scroller { - color: ORANGE, + color: SCROLLER, ..active.scroller }, ..active @@ -169,7 +167,7 @@ mod dark { scrollable::Scrollbar { scroller: scrollable::Scroller { - color: ORANGE_DARK, + color: ACCENT, ..hovered.scroller }, ..hovered @@ -182,10 +180,10 @@ mod dark { impl rule::StyleSheet for Rule { fn style(&self) -> rule::Style { rule::Style { - color: CYAN, + color: SURFACE, width: 2, radius: 1, - fill_mode: rule::FillMode::Percent(15.0), + fill_mode: rule::FillMode::Percent(30.0), } } } -- cgit From 2ca05520bafdaa3377c5571db46ab41aac9d0290 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Sat, 24 Oct 2020 10:32:26 +0200 Subject: Update screenshot of `scrollable` example --- examples/scrollable/screenshot.png | Bin 238486 -> 148253 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/examples/scrollable/screenshot.png b/examples/scrollable/screenshot.png index f2add949..2d800251 100644 Binary files a/examples/scrollable/screenshot.png and b/examples/scrollable/screenshot.png differ -- cgit