... and also make `center_x` and `center_y` set `width` and `height` to `Length::Fill`, respectively. This targets the most common use case when centering things and removes a bunch of boilerplate as a result.
350 lines
12 KiB
Rust
350 lines
12 KiB
Rust
use iced::widget::scrollable::Properties;
|
|
use iced::widget::{
|
|
button, column, container, horizontal_space, progress_bar, radio, row,
|
|
scrollable, slider, text, vertical_space, Scrollable,
|
|
};
|
|
use iced::{Alignment, Border, Color, Command, Element, Length, Theme};
|
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
static SCROLLABLE_ID: Lazy<scrollable::Id> = Lazy::new(scrollable::Id::unique);
|
|
|
|
pub fn main() -> iced::Result {
|
|
iced::program(
|
|
"Scrollable - Iced",
|
|
ScrollableDemo::update,
|
|
ScrollableDemo::view,
|
|
)
|
|
.theme(ScrollableDemo::theme)
|
|
.run()
|
|
}
|
|
|
|
struct ScrollableDemo {
|
|
scrollable_direction: Direction,
|
|
scrollbar_width: u16,
|
|
scrollbar_margin: u16,
|
|
scroller_width: u16,
|
|
current_scroll_offset: scrollable::RelativeOffset,
|
|
alignment: scrollable::Alignment,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq, Copy)]
|
|
enum Direction {
|
|
Vertical,
|
|
Horizontal,
|
|
Multi,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Message {
|
|
SwitchDirection(Direction),
|
|
AlignmentChanged(scrollable::Alignment),
|
|
ScrollbarWidthChanged(u16),
|
|
ScrollbarMarginChanged(u16),
|
|
ScrollerWidthChanged(u16),
|
|
ScrollToBeginning,
|
|
ScrollToEnd,
|
|
Scrolled(scrollable::Viewport),
|
|
}
|
|
|
|
impl ScrollableDemo {
|
|
fn new() -> Self {
|
|
ScrollableDemo {
|
|
scrollable_direction: Direction::Vertical,
|
|
scrollbar_width: 10,
|
|
scrollbar_margin: 0,
|
|
scroller_width: 10,
|
|
current_scroll_offset: scrollable::RelativeOffset::START,
|
|
alignment: scrollable::Alignment::Start,
|
|
}
|
|
}
|
|
|
|
fn update(&mut self, message: Message) -> Command<Message> {
|
|
match message {
|
|
Message::SwitchDirection(direction) => {
|
|
self.current_scroll_offset = scrollable::RelativeOffset::START;
|
|
self.scrollable_direction = direction;
|
|
|
|
scrollable::snap_to(
|
|
SCROLLABLE_ID.clone(),
|
|
self.current_scroll_offset,
|
|
)
|
|
}
|
|
Message::AlignmentChanged(alignment) => {
|
|
self.current_scroll_offset = scrollable::RelativeOffset::START;
|
|
self.alignment = alignment;
|
|
|
|
scrollable::snap_to(
|
|
SCROLLABLE_ID.clone(),
|
|
self.current_scroll_offset,
|
|
)
|
|
}
|
|
Message::ScrollbarWidthChanged(width) => {
|
|
self.scrollbar_width = width;
|
|
|
|
Command::none()
|
|
}
|
|
Message::ScrollbarMarginChanged(margin) => {
|
|
self.scrollbar_margin = margin;
|
|
|
|
Command::none()
|
|
}
|
|
Message::ScrollerWidthChanged(width) => {
|
|
self.scroller_width = width;
|
|
|
|
Command::none()
|
|
}
|
|
Message::ScrollToBeginning => {
|
|
self.current_scroll_offset = scrollable::RelativeOffset::START;
|
|
|
|
scrollable::snap_to(
|
|
SCROLLABLE_ID.clone(),
|
|
self.current_scroll_offset,
|
|
)
|
|
}
|
|
Message::ScrollToEnd => {
|
|
self.current_scroll_offset = scrollable::RelativeOffset::END;
|
|
|
|
scrollable::snap_to(
|
|
SCROLLABLE_ID.clone(),
|
|
self.current_scroll_offset,
|
|
)
|
|
}
|
|
Message::Scrolled(viewport) => {
|
|
self.current_scroll_offset = viewport.relative_offset();
|
|
|
|
Command::none()
|
|
}
|
|
}
|
|
}
|
|
|
|
fn view(&self) -> Element<Message> {
|
|
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 scroll_slider_controls = column![
|
|
text("Scrollbar width:"),
|
|
scrollbar_width_slider,
|
|
text("Scrollbar margin:"),
|
|
scrollbar_margin_slider,
|
|
text("Scroller width:"),
|
|
scroller_width_slider,
|
|
]
|
|
.spacing(10);
|
|
|
|
let scroll_orientation_controls = column![
|
|
text("Scrollbar direction:"),
|
|
radio(
|
|
"Vertical",
|
|
Direction::Vertical,
|
|
Some(self.scrollable_direction),
|
|
Message::SwitchDirection,
|
|
),
|
|
radio(
|
|
"Horizontal",
|
|
Direction::Horizontal,
|
|
Some(self.scrollable_direction),
|
|
Message::SwitchDirection,
|
|
),
|
|
radio(
|
|
"Both!",
|
|
Direction::Multi,
|
|
Some(self.scrollable_direction),
|
|
Message::SwitchDirection,
|
|
),
|
|
]
|
|
.spacing(10);
|
|
|
|
let scroll_alignment_controls = column![
|
|
text("Scrollable alignment:"),
|
|
radio(
|
|
"Start",
|
|
scrollable::Alignment::Start,
|
|
Some(self.alignment),
|
|
Message::AlignmentChanged,
|
|
),
|
|
radio(
|
|
"End",
|
|
scrollable::Alignment::End,
|
|
Some(self.alignment),
|
|
Message::AlignmentChanged,
|
|
)
|
|
]
|
|
.spacing(10);
|
|
|
|
let scroll_controls = row![
|
|
scroll_slider_controls,
|
|
scroll_orientation_controls,
|
|
scroll_alignment_controls
|
|
]
|
|
.spacing(20);
|
|
|
|
let scroll_to_end_button = || {
|
|
button("Scroll to end")
|
|
.padding(10)
|
|
.on_press(Message::ScrollToEnd)
|
|
};
|
|
|
|
let scroll_to_beginning_button = || {
|
|
button("Scroll to beginning")
|
|
.padding(10)
|
|
.on_press(Message::ScrollToBeginning)
|
|
};
|
|
|
|
let scrollable_content: Element<Message> =
|
|
Element::from(match self.scrollable_direction {
|
|
Direction::Vertical => Scrollable::with_direction(
|
|
column![
|
|
scroll_to_end_button(),
|
|
text("Beginning!"),
|
|
vertical_space().height(1200),
|
|
text("Middle!"),
|
|
vertical_space().height(1200),
|
|
text("End!"),
|
|
scroll_to_beginning_button(),
|
|
]
|
|
.align_items(Alignment::Center)
|
|
.padding([40, 0, 40, 0])
|
|
.spacing(40),
|
|
scrollable::Direction::Vertical(
|
|
Properties::new()
|
|
.width(self.scrollbar_width)
|
|
.margin(self.scrollbar_margin)
|
|
.scroller_width(self.scroller_width)
|
|
.alignment(self.alignment),
|
|
),
|
|
)
|
|
.width(Length::Fill)
|
|
.height(Length::Fill)
|
|
.id(SCROLLABLE_ID.clone())
|
|
.on_scroll(Message::Scrolled),
|
|
Direction::Horizontal => Scrollable::with_direction(
|
|
row![
|
|
scroll_to_end_button(),
|
|
text("Beginning!"),
|
|
horizontal_space().width(1200),
|
|
text("Middle!"),
|
|
horizontal_space().width(1200),
|
|
text("End!"),
|
|
scroll_to_beginning_button(),
|
|
]
|
|
.height(450)
|
|
.align_items(Alignment::Center)
|
|
.padding([0, 40, 0, 40])
|
|
.spacing(40),
|
|
scrollable::Direction::Horizontal(
|
|
Properties::new()
|
|
.width(self.scrollbar_width)
|
|
.margin(self.scrollbar_margin)
|
|
.scroller_width(self.scroller_width)
|
|
.alignment(self.alignment),
|
|
),
|
|
)
|
|
.width(Length::Fill)
|
|
.height(Length::Fill)
|
|
.id(SCROLLABLE_ID.clone())
|
|
.on_scroll(Message::Scrolled),
|
|
Direction::Multi => Scrollable::with_direction(
|
|
//horizontal content
|
|
row![
|
|
column![
|
|
text("Let's do some scrolling!"),
|
|
vertical_space().height(2400)
|
|
],
|
|
scroll_to_end_button(),
|
|
text("Horizontal - Beginning!"),
|
|
horizontal_space().width(1200),
|
|
//vertical content
|
|
column![
|
|
text("Horizontal - Middle!"),
|
|
scroll_to_end_button(),
|
|
text("Vertical - Beginning!"),
|
|
vertical_space().height(1200),
|
|
text("Vertical - Middle!"),
|
|
vertical_space().height(1200),
|
|
text("Vertical - End!"),
|
|
scroll_to_beginning_button(),
|
|
vertical_space().height(40),
|
|
]
|
|
.spacing(40),
|
|
horizontal_space().width(1200),
|
|
text("Horizontal - End!"),
|
|
scroll_to_beginning_button(),
|
|
]
|
|
.align_items(Alignment::Center)
|
|
.padding([0, 40, 0, 40])
|
|
.spacing(40),
|
|
{
|
|
let properties = Properties::new()
|
|
.width(self.scrollbar_width)
|
|
.margin(self.scrollbar_margin)
|
|
.scroller_width(self.scroller_width)
|
|
.alignment(self.alignment);
|
|
|
|
scrollable::Direction::Both {
|
|
horizontal: properties,
|
|
vertical: properties,
|
|
}
|
|
},
|
|
)
|
|
.width(Length::Fill)
|
|
.height(Length::Fill)
|
|
.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(progress_bar_custom_style)
|
|
.into()
|
|
}
|
|
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(progress_bar_custom_style)
|
|
]
|
|
.spacing(10)
|
|
.into(),
|
|
};
|
|
|
|
let content: Element<Message> =
|
|
column![scroll_controls, scrollable_content, progress_bars]
|
|
.align_items(Alignment::Center)
|
|
.spacing(10)
|
|
.into();
|
|
|
|
container(content).padding(20).into()
|
|
}
|
|
|
|
fn theme(&self) -> Theme {
|
|
Theme::Dark
|
|
}
|
|
}
|
|
|
|
impl Default for ScrollableDemo {
|
|
fn default() -> Self {
|
|
Self::new()
|
|
}
|
|
}
|
|
|
|
fn progress_bar_custom_style(theme: &Theme) -> progress_bar::Style {
|
|
progress_bar::Style {
|
|
background: theme.extended_palette().background.strong.color.into(),
|
|
bar: Color::from_rgb8(250, 85, 134).into(),
|
|
border: Border::default(),
|
|
}
|
|
}
|