summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
authorLibravatar Bingus <shankern@protonmail.com>2022-11-19 10:29:37 -0800
committerLibravatar bungoboingo <shankern@protonmail.com>2022-12-29 10:21:23 -0800
commitd91f4f6aa74d0693179a02167d626efa3ac4c20b (patch)
tree293852763793fbf6cb142fffac222ee7a87a1edf /examples
parenta6d0d5773f0561a841a84b538523cbd97e91eccd (diff)
downloadiced-d91f4f6aa74d0693179a02167d626efa3ac4c20b.tar.gz
iced-d91f4f6aa74d0693179a02167d626efa3ac4c20b.tar.bz2
iced-d91f4f6aa74d0693179a02167d626efa3ac4c20b.zip
Add multidirectional scrolling capabilities to the existing Scrollable.
Diffstat (limited to 'examples')
-rw-r--r--examples/README.md2
-rw-r--r--examples/scrollable/Cargo.toml1
-rw-r--r--examples/scrollable/screenshot.pngbin104995 -> 521151 bytes
-rw-r--r--examples/scrollable/src/main.rs506
-rw-r--r--examples/websocket/src/main.rs7
5 files changed, 330 insertions, 186 deletions
diff --git a/examples/README.md b/examples/README.md
index bb15dc2e..74cf145b 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -99,7 +99,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.
+- [`scrollable`](scrollable), a showcase of various scrollable content configurations.
- [`sierpinski_triangle`](sierpinski_triangle), a [sierpiński triangle](https://en.wikipedia.org/wiki/Sierpi%C5%84ski_triangle) Emulator, use `Canvas` and `Slider`.
- [`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.
diff --git a/examples/scrollable/Cargo.toml b/examples/scrollable/Cargo.toml
index 610c13b4..4bef4281 100644
--- a/examples/scrollable/Cargo.toml
+++ b/examples/scrollable/Cargo.toml
@@ -7,3 +7,4 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["debug"] }
+lazy_static = "1.4"
diff --git a/examples/scrollable/screenshot.png b/examples/scrollable/screenshot.png
index e91fd565..ee044447 100644
--- a/examples/scrollable/screenshot.png
+++ b/examples/scrollable/screenshot.png
Binary files differ
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index 6eba34e2..1481afcc 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -1,44 +1,60 @@
-use iced::executor;
+use iced::widget::scrollable::{Scrollbar, Scroller};
use iced::widget::{
- button, column, container, horizontal_rule, progress_bar, radio,
- scrollable, text, vertical_space, Row,
+ button, column, container, horizontal_space, progress_bar, radio, row,
+ scrollable, slider, text, vertical_space,
};
+use iced::{executor, theme, Alignment, Color, Vector};
use iced::{Application, Command, Element, Length, Settings, Theme};
+use lazy_static::lazy_static;
+
+lazy_static! {
+ static ref SCROLLABLE_ID: scrollable::Id = scrollable::Id::unique();
+}
pub fn main() -> iced::Result {
ScrollableDemo::run(Settings::default())
}
struct ScrollableDemo {
- theme: Theme,
- variants: Vec<Variant>,
+ scrollable_direction: Direction,
+ scrollbar_width: u16,
+ scrollbar_margin: u16,
+ scroller_width: u16,
+ current_scroll_offset: Vector<f32>,
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum ThemeType {
- Light,
- Dark,
+#[derive(Debug, Clone, Eq, PartialEq, Copy)]
+enum Direction {
+ Vertical,
+ Horizontal,
+ Multi,
}
#[derive(Debug, Clone)]
enum Message {
- ThemeChanged(ThemeType),
- ScrollToTop(usize),
- ScrollToBottom(usize),
- Scrolled(usize, f32),
+ SwitchDirection(Direction),
+ ScrollbarWidthChanged(u16),
+ ScrollbarMarginChanged(u16),
+ ScrollerWidthChanged(u16),
+ ScrollToBeginning(scrollable::Direction),
+ ScrollToEnd(scrollable::Direction),
+ Scrolled(Vector<f32>),
}
impl Application for ScrollableDemo {
+ type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
- type Executor = executor::Default;
type Flags = ();
fn new(_flags: Self::Flags) -> (Self, Command<Message>) {
(
ScrollableDemo {
- theme: Default::default(),
- variants: Variant::all(),
+ scrollable_direction: Direction::Vertical,
+ scrollbar_width: 10,
+ scrollbar_margin: 0,
+ scroller_width: 10,
+ current_scroll_offset: Vector::new(0.0, 0.0),
},
Command::none(),
)
@@ -50,209 +66,333 @@ impl Application for ScrollableDemo {
fn update(&mut self, message: Message) -> Command<Message> {
match message {
- Message::ThemeChanged(theme) => {
- self.theme = match theme {
- ThemeType::Light => Theme::Light,
- ThemeType::Dark => Theme::Dark,
- };
+ Message::SwitchDirection(direction) => {
+ self.current_scroll_offset = Vector::new(0.0, 0.0);
+ self.scrollable_direction = direction;
Command::none()
}
- Message::ScrollToTop(i) => {
- if let Some(variant) = self.variants.get_mut(i) {
- variant.latest_offset = 0.0;
+ Message::ScrollbarWidthChanged(width) => {
+ self.scrollbar_width = width;
- scrollable::snap_to(Variant::id(i), 0.0)
- } else {
- Command::none()
- }
+ Command::none()
+ }
+ Message::ScrollbarMarginChanged(margin) => {
+ self.scrollbar_margin = margin;
+
+ Command::none()
}
- Message::ScrollToBottom(i) => {
- if let Some(variant) = self.variants.get_mut(i) {
- variant.latest_offset = 1.0;
+ Message::ScrollerWidthChanged(width) => {
+ self.scroller_width = width;
- scrollable::snap_to(Variant::id(i), 1.0)
- } else {
- Command::none()
+ Command::none()
+ }
+ Message::ScrollToBeginning(direction) => {
+ match direction {
+ scrollable::Direction::Horizontal => {
+ self.current_scroll_offset.x = 0.0;
+ }
+ scrollable::Direction::Vertical => {
+ self.current_scroll_offset.y = 0.0;
+ }
}
+
+ scrollable::snap_to(
+ SCROLLABLE_ID.clone(),
+ Vector::new(
+ self.current_scroll_offset.x,
+ self.current_scroll_offset.y,
+ ),
+ )
}
- Message::Scrolled(i, offset) => {
- if let Some(variant) = self.variants.get_mut(i) {
- variant.latest_offset = offset;
+ Message::ScrollToEnd(direction) => {
+ match direction {
+ scrollable::Direction::Horizontal => {
+ self.current_scroll_offset.x = 1.0;
+ }
+ scrollable::Direction::Vertical => {
+ self.current_scroll_offset.y = 1.0;
+ }
}
+ scrollable::snap_to(
+ SCROLLABLE_ID.clone(),
+ Vector::new(
+ self.current_scroll_offset.x,
+ self.current_scroll_offset.y,
+ ),
+ )
+ }
+ Message::Scrolled(offset) => {
+ self.current_scroll_offset = offset;
+
Command::none()
}
}
}
fn view(&self) -> Element<Message> {
- let ScrollableDemo { variants, .. } = self;
-
- let choose_theme = [ThemeType::Light, ThemeType::Dark].iter().fold(
- column!["Choose a theme:"].spacing(10),
- |column, option| {
- column.push(radio(
- format!("{:?}", option),
- *option,
- Some(*option),
- Message::ThemeChanged,
- ))
- },
+ let scrollbar_width_slider = slider(
+ 0..=15,
+ self.scrollbar_width,
+ Message::ScrollbarWidthChanged,
);
+ let scrollbar_margin_slider = slider(
+ 0..=15,
+ self.scrollbar_margin,
+ Message::ScrollbarMarginChanged,
+ );
+ let scroller_width_slider =
+ slider(0..=15, self.scroller_width, Message::ScrollerWidthChanged);
- let scrollable_row = Row::with_children(
- variants
- .iter()
- .enumerate()
- .map(|(i, variant)| {
- let mut contents = column![
- variant.title,
- button("Scroll to bottom",)
- .width(Length::Fill)
- .padding(10)
- .on_press(Message::ScrollToBottom(i)),
- ]
- .padding(10)
- .spacing(10)
- .width(Length::Fill);
-
- if let Some(scrollbar_width) = variant.scrollbar_width {
- contents = contents.push(text(format!(
- "scrollbar_width: {:?}",
- scrollbar_width
- )));
- }
-
- if let Some(scrollbar_margin) = variant.scrollbar_margin {
- contents = contents.push(text(format!(
- "scrollbar_margin: {:?}",
- scrollbar_margin
- )));
- }
+ let scroll_slider_controls = column![
+ text("Scrollbar width:"),
+ scrollbar_width_slider,
+ text("Scrollbar margin:"),
+ scrollbar_margin_slider,
+ text("Scroller width:"),
+ scroller_width_slider,
+ ]
+ .width(Length::Fill);
- if let Some(scroller_width) = variant.scroller_width {
- contents = contents.push(text(format!(
- "scroller_width: {:?}",
- scroller_width
- )));
- }
+ let scroll_orientation_controls = column(vec![
+ text("Scrollbar direction:").into(),
+ radio(
+ "Vertical",
+ Direction::Vertical,
+ Some(self.scrollable_direction),
+ Message::SwitchDirection,
+ )
+ .into(),
+ radio(
+ "Horizontal",
+ Direction::Horizontal,
+ Some(self.scrollable_direction),
+ Message::SwitchDirection,
+ )
+ .into(),
+ radio(
+ "Both!",
+ Direction::Multi,
+ Some(self.scrollable_direction),
+ Message::SwitchDirection,
+ )
+ .into(),
+ ])
+ .width(Length::Fill);
- contents = contents
- .push(vertical_space(Length::Units(100)))
- .push(
- "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(vertical_space(Length::Units(1200)))
- .push("Middle")
- .push(vertical_space(Length::Units(1200)))
- .push("The End.")
- .push(
- button("Scroll to top")
- .width(Length::Fill)
- .padding(10)
- .on_press(Message::ScrollToTop(i)),
- );
-
- let mut scrollable = scrollable(contents)
- .id(Variant::id(i))
- .height(Length::Fill)
- .on_scroll(move |offset| Message::Scrolled(i, offset));
-
- if let Some(scrollbar_width) = variant.scrollbar_width {
- scrollable =
- scrollable.scrollbar_width(scrollbar_width);
- }
+ let scroll_controls =
+ row![scroll_slider_controls, scroll_orientation_controls]
+ .spacing(20)
+ .width(Length::Fill);
- if let Some(scrollbar_margin) = variant.scrollbar_margin {
- scrollable =
- scrollable.scrollbar_margin(scrollbar_margin);
- }
+ let scroll_to_end_button = |direction: scrollable::Direction| {
+ button("Scroll to end")
+ .padding(10)
+ .width(Length::Units(120))
+ .on_press(Message::ScrollToEnd(direction))
+ };
- if let Some(scroller_width) = variant.scroller_width {
- scrollable = scrollable.scroller_width(scroller_width);
- }
+ let scroll_to_beginning_button = |direction: scrollable::Direction| {
+ button("Scroll to beginning")
+ .padding(10)
+ .width(Length::Units(120))
+ .on_press(Message::ScrollToBeginning(direction))
+ };
+ let scrollable_content: Element<Message> =
+ Element::from(match self.scrollable_direction {
+ Direction::Vertical => scrollable(
column![
- scrollable,
- progress_bar(0.0..=1.0, variant.latest_offset,)
+ scroll_to_end_button(scrollable::Direction::Vertical),
+ text("Beginning!"),
+ vertical_space(Length::Units(1200)),
+ text("Middle!"),
+ vertical_space(Length::Units(1200)),
+ text("End!"),
+ scroll_to_beginning_button(
+ scrollable::Direction::Vertical
+ ),
]
.width(Length::Fill)
- .height(Length::Fill)
- .spacing(10)
+ .align_items(Alignment::Center)
+ .padding([40, 0, 40, 0])
+ .spacing(40),
+ )
+ .height(Length::Fill)
+ .scrollbar_width(self.scrollbar_width)
+ .scrollbar_margin(self.scrollbar_margin)
+ .scroller_width(self.scroller_width)
+ .id(SCROLLABLE_ID.clone())
+ .on_scroll(Message::Scrolled),
+ Direction::Horizontal => scrollable(
+ row![
+ scroll_to_end_button(scrollable::Direction::Horizontal),
+ text("Beginning!"),
+ horizontal_space(Length::Units(1200)),
+ text("Middle!"),
+ horizontal_space(Length::Units(1200)),
+ text("End!"),
+ scroll_to_beginning_button(
+ scrollable::Direction::Horizontal
+ ),
+ ]
+ .height(Length::Units(450))
+ .align_items(Alignment::Center)
+ .padding([0, 40, 0, 40])
+ .spacing(40),
+ )
+ .height(Length::Fill)
+ .horizontal_scroll(
+ scrollable::Horizontal::new()
+ .scrollbar_height(self.scrollbar_width)
+ .scrollbar_margin(self.scrollbar_margin)
+ .scroller_height(self.scroller_width),
+ )
+ .style(theme::Scrollable::Custom(Box::new(
+ ScrollbarCustomStyle,
+ )))
+ .id(SCROLLABLE_ID.clone())
+ .on_scroll(Message::Scrolled),
+ Direction::Multi => scrollable(
+ //horizontal content
+ row![
+ column![
+ text("Let's do some scrolling!"),
+ vertical_space(Length::Units(2400))
+ ],
+ scroll_to_end_button(scrollable::Direction::Horizontal),
+ text("Horizontal - Beginning!"),
+ horizontal_space(Length::Units(1200)),
+ //vertical content
+ column![
+ text("Horizontal - Middle!"),
+ scroll_to_end_button(
+ scrollable::Direction::Vertical
+ ),
+ text("Vertical - Beginning!"),
+ vertical_space(Length::Units(1200)),
+ text("Vertical - Middle!"),
+ vertical_space(Length::Units(1200)),
+ text("Vertical - End!"),
+ scroll_to_beginning_button(
+ scrollable::Direction::Vertical
+ )
+ ]
+ .align_items(Alignment::Fill)
+ .spacing(40),
+ horizontal_space(Length::Units(1200)),
+ text("Horizontal - End!"),
+ scroll_to_beginning_button(
+ scrollable::Direction::Horizontal
+ ),
+ ]
+ .align_items(Alignment::Center)
+ .padding([0, 40, 0, 40])
+ .spacing(40),
+ )
+ .height(Length::Fill)
+ .scrollbar_width(self.scrollbar_width)
+ .scrollbar_margin(self.scrollbar_margin)
+ .scroller_width(self.scroller_width)
+ .horizontal_scroll(
+ scrollable::Horizontal::new()
+ .scrollbar_height(self.scrollbar_width)
+ .scrollbar_margin(self.scrollbar_margin)
+ .scroller_height(self.scroller_width),
+ )
+ .style(theme::Scrollable::Custom(Box::new(
+ ScrollbarCustomStyle,
+ )))
+ .id(SCROLLABLE_ID.clone())
+ .on_scroll(Message::Scrolled),
+ });
+
+ let progress_bars: Element<Message> = match self.scrollable_direction {
+ Direction::Vertical => {
+ progress_bar(0.0..=1.0, self.current_scroll_offset.y).into()
+ }
+ Direction::Horizontal => {
+ progress_bar(0.0..=1.0, self.current_scroll_offset.x)
+ .style(theme::ProgressBar::Custom(Box::new(
+ ProgressBarCustomStyle,
+ )))
.into()
- })
- .collect(),
- )
- .spacing(20)
- .width(Length::Fill)
- .height(Length::Fill);
+ }
+ Direction::Multi => column![
+ progress_bar(0.0..=1.0, self.current_scroll_offset.y),
+ progress_bar(0.0..=1.0, self.current_scroll_offset.x).style(
+ theme::ProgressBar::Custom(Box::new(
+ ProgressBarCustomStyle,
+ ))
+ )
+ ]
+ .spacing(10)
+ .into(),
+ };
- let content =
- column![choose_theme, horizontal_rule(20), scrollable_row]
- .spacing(20)
- .padding(20);
-
- container(content)
- .width(Length::Fill)
- .height(Length::Fill)
- .center_x()
- .center_y()
- .into()
+ let content: Element<Message> =
+ column![scroll_controls, scrollable_content, progress_bars]
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .align_items(Alignment::Center)
+ .spacing(10)
+ .into();
+
+ Element::from(
+ container(content)
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .padding(40)
+ .center_x()
+ .center_y(),
+ )
}
- fn theme(&self) -> Theme {
- self.theme.clone()
+ fn theme(&self) -> Self::Theme {
+ Theme::Dark
}
}
-/// A version of a scrollable
-struct Variant {
- title: &'static str,
- scrollbar_width: Option<u16>,
- scrollbar_margin: Option<u16>,
- scroller_width: Option<u16>,
- latest_offset: f32,
-}
+struct ScrollbarCustomStyle;
-impl Variant {
- pub fn all() -> Vec<Self> {
- vec![
- Self {
- title: "Default Scrollbar",
- scrollbar_width: None,
- scrollbar_margin: None,
- scroller_width: None,
- latest_offset: 0.0,
- },
- Self {
- title: "Slimmed & Margin",
- scrollbar_width: Some(4),
- scrollbar_margin: Some(3),
- scroller_width: Some(4),
- latest_offset: 0.0,
- },
- Self {
- title: "Wide Scroller",
- scrollbar_width: Some(4),
- scrollbar_margin: None,
- scroller_width: Some(10),
- latest_offset: 0.0,
- },
- Self {
- title: "Narrow Scroller",
- scrollbar_width: Some(10),
- scrollbar_margin: None,
- scroller_width: Some(4),
- latest_offset: 0.0,
+impl scrollable::StyleSheet for ScrollbarCustomStyle {
+ type Style = Theme;
+
+ fn active(&self, style: &Self::Style) -> Scrollbar {
+ style.active(&theme::Scrollable::Default)
+ }
+
+ fn hovered(&self, style: &Self::Style) -> Scrollbar {
+ style.hovered(&theme::Scrollable::Default)
+ }
+
+ fn hovered_horizontal(&self, style: &Self::Style) -> Scrollbar {
+ Scrollbar {
+ background: style.active(&theme::Scrollable::default()).background,
+ border_radius: 0.0,
+ border_width: 0.0,
+ border_color: Default::default(),
+ scroller: Scroller {
+ color: Color::from_rgb8(250, 85, 134),
+ border_radius: 0.0,
+ border_width: 0.0,
+ border_color: Default::default(),
},
- ]
+ }
}
+}
- pub fn id(i: usize) -> scrollable::Id {
- scrollable::Id::new(format!("scrollable-{}", i))
+struct ProgressBarCustomStyle;
+
+impl progress_bar::StyleSheet for ProgressBarCustomStyle {
+ type Style = Theme;
+
+ fn appearance(&self, style: &Self::Style) -> progress_bar::Appearance {
+ progress_bar::Appearance {
+ background: style.extended_palette().background.strong.color.into(),
+ bar: Color::from_rgb8(250, 85, 134).into(),
+ border_radius: 0.0,
+ }
}
}
diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs
index ff2929da..b10ef17e 100644
--- a/examples/websocket/src/main.rs
+++ b/examples/websocket/src/main.rs
@@ -1,10 +1,10 @@
mod echo;
use iced::alignment::{self, Alignment};
-use iced::executor;
use iced::widget::{
button, column, container, row, scrollable, text, text_input, Column,
};
+use iced::{executor, Vector};
use iced::{
Application, Color, Command, Element, Length, Settings, Subscription, Theme,
};
@@ -81,7 +81,10 @@ impl Application for WebSocket {
echo::Event::MessageReceived(message) => {
self.messages.push(message);
- scrollable::snap_to(MESSAGE_LOG.clone(), 1.0)
+ scrollable::snap_to(
+ MESSAGE_LOG.clone(),
+ Vector::new(0.0, 1.0),
+ )
}
},
Message::Server => Command::none(),