... 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.
305 lines
7.2 KiB
Rust
305 lines
7.2 KiB
Rust
use iced::keyboard;
|
|
use iced::mouse;
|
|
use iced::widget::{
|
|
button, canvas, center, checkbox, column, container, horizontal_space,
|
|
pick_list, row, scrollable, text,
|
|
};
|
|
use iced::{
|
|
color, Alignment, Element, Font, Length, Point, Rectangle, Renderer,
|
|
Subscription, Theme,
|
|
};
|
|
|
|
pub fn main() -> iced::Result {
|
|
iced::program(Layout::title, Layout::update, Layout::view)
|
|
.subscription(Layout::subscription)
|
|
.theme(Layout::theme)
|
|
.run()
|
|
}
|
|
|
|
#[derive(Default, Debug)]
|
|
struct Layout {
|
|
example: Example,
|
|
explain: bool,
|
|
theme: Theme,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
enum Message {
|
|
Next,
|
|
Previous,
|
|
ExplainToggled(bool),
|
|
ThemeSelected(Theme),
|
|
}
|
|
|
|
impl Layout {
|
|
fn title(&self) -> String {
|
|
format!("{} - Layout - Iced", self.example.title)
|
|
}
|
|
|
|
fn update(&mut self, message: Message) {
|
|
match message {
|
|
Message::Next => {
|
|
self.example = self.example.next();
|
|
}
|
|
Message::Previous => {
|
|
self.example = self.example.previous();
|
|
}
|
|
Message::ExplainToggled(explain) => {
|
|
self.explain = explain;
|
|
}
|
|
Message::ThemeSelected(theme) => {
|
|
self.theme = theme;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn subscription(&self) -> Subscription<Message> {
|
|
use keyboard::key;
|
|
|
|
keyboard::on_key_release(|key, _modifiers| match key {
|
|
keyboard::Key::Named(key::Named::ArrowLeft) => {
|
|
Some(Message::Previous)
|
|
}
|
|
keyboard::Key::Named(key::Named::ArrowRight) => Some(Message::Next),
|
|
_ => None,
|
|
})
|
|
}
|
|
|
|
fn view(&self) -> Element<Message> {
|
|
let header = row![
|
|
text(self.example.title).size(20).font(Font::MONOSPACE),
|
|
horizontal_space(),
|
|
checkbox("Explain", self.explain)
|
|
.on_toggle(Message::ExplainToggled),
|
|
pick_list(Theme::ALL, Some(&self.theme), Message::ThemeSelected),
|
|
]
|
|
.spacing(20)
|
|
.align_items(Alignment::Center);
|
|
|
|
let example = center(if self.explain {
|
|
self.example.view().explain(color!(0x0000ff))
|
|
} else {
|
|
self.example.view()
|
|
})
|
|
.style(|theme| {
|
|
let palette = theme.extended_palette();
|
|
|
|
container::Style::default()
|
|
.with_border(palette.background.strong.color, 4.0)
|
|
})
|
|
.padding(4);
|
|
|
|
let controls = row([
|
|
(!self.example.is_first()).then_some(
|
|
button("← Previous")
|
|
.padding([5, 10])
|
|
.on_press(Message::Previous)
|
|
.into(),
|
|
),
|
|
Some(horizontal_space().into()),
|
|
(!self.example.is_last()).then_some(
|
|
button("Next →")
|
|
.padding([5, 10])
|
|
.on_press(Message::Next)
|
|
.into(),
|
|
),
|
|
]
|
|
.into_iter()
|
|
.flatten());
|
|
|
|
column![header, example, controls]
|
|
.spacing(10)
|
|
.padding(20)
|
|
.into()
|
|
}
|
|
|
|
fn theme(&self) -> Theme {
|
|
self.theme.clone()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
struct Example {
|
|
title: &'static str,
|
|
view: fn() -> Element<'static, Message>,
|
|
}
|
|
|
|
impl Example {
|
|
const LIST: &'static [Self] = &[
|
|
Self {
|
|
title: "Centered",
|
|
view: centered,
|
|
},
|
|
Self {
|
|
title: "Column",
|
|
view: column_,
|
|
},
|
|
Self {
|
|
title: "Row",
|
|
view: row_,
|
|
},
|
|
Self {
|
|
title: "Space",
|
|
view: space,
|
|
},
|
|
Self {
|
|
title: "Application",
|
|
view: application,
|
|
},
|
|
];
|
|
|
|
fn is_first(self) -> bool {
|
|
Self::LIST.first() == Some(&self)
|
|
}
|
|
|
|
fn is_last(self) -> bool {
|
|
Self::LIST.last() == Some(&self)
|
|
}
|
|
|
|
fn previous(self) -> Self {
|
|
let Some(index) =
|
|
Self::LIST.iter().position(|&example| example == self)
|
|
else {
|
|
return self;
|
|
};
|
|
|
|
Self::LIST
|
|
.get(index.saturating_sub(1))
|
|
.copied()
|
|
.unwrap_or(self)
|
|
}
|
|
|
|
fn next(self) -> Self {
|
|
let Some(index) =
|
|
Self::LIST.iter().position(|&example| example == self)
|
|
else {
|
|
return self;
|
|
};
|
|
|
|
Self::LIST.get(index + 1).copied().unwrap_or(self)
|
|
}
|
|
|
|
fn view(&self) -> Element<Message> {
|
|
(self.view)()
|
|
}
|
|
}
|
|
|
|
impl Default for Example {
|
|
fn default() -> Self {
|
|
Self::LIST[0]
|
|
}
|
|
}
|
|
|
|
fn centered<'a>() -> Element<'a, Message> {
|
|
center(text("I am centered!").size(50)).into()
|
|
}
|
|
|
|
fn column_<'a>() -> Element<'a, Message> {
|
|
column![
|
|
"A column can be used to",
|
|
"lay out widgets vertically.",
|
|
square(50),
|
|
square(50),
|
|
square(50),
|
|
"The amount of space between",
|
|
"elements can be configured!",
|
|
]
|
|
.spacing(40)
|
|
.into()
|
|
}
|
|
|
|
fn row_<'a>() -> Element<'a, Message> {
|
|
row![
|
|
"A row works like a column...",
|
|
square(50),
|
|
square(50),
|
|
square(50),
|
|
"but lays out widgets horizontally!",
|
|
]
|
|
.spacing(40)
|
|
.into()
|
|
}
|
|
|
|
fn space<'a>() -> Element<'a, Message> {
|
|
row!["Left!", horizontal_space(), "Right!"].into()
|
|
}
|
|
|
|
fn application<'a>() -> Element<'a, Message> {
|
|
let header = container(
|
|
row![
|
|
square(40),
|
|
horizontal_space(),
|
|
"Header!",
|
|
horizontal_space(),
|
|
square(40),
|
|
]
|
|
.padding(10)
|
|
.align_items(Alignment::Center),
|
|
)
|
|
.style(|theme| {
|
|
let palette = theme.extended_palette();
|
|
|
|
container::Style::default()
|
|
.with_border(palette.background.strong.color, 1)
|
|
});
|
|
|
|
let sidebar = container(
|
|
column!["Sidebar!", square(50), square(50)]
|
|
.spacing(40)
|
|
.padding(10)
|
|
.width(200)
|
|
.align_items(Alignment::Center),
|
|
)
|
|
.style(container::rounded_box)
|
|
.center_y();
|
|
|
|
let content = container(
|
|
scrollable(
|
|
column![
|
|
"Content!",
|
|
square(400),
|
|
square(200),
|
|
square(400),
|
|
"The end"
|
|
]
|
|
.spacing(40)
|
|
.align_items(Alignment::Center)
|
|
.width(Length::Fill),
|
|
)
|
|
.height(Length::Fill),
|
|
)
|
|
.padding(10);
|
|
|
|
column![header, row![sidebar, content]].into()
|
|
}
|
|
|
|
fn square<'a>(size: impl Into<Length> + Copy) -> Element<'a, Message> {
|
|
struct Square;
|
|
|
|
impl canvas::Program<Message> for Square {
|
|
type State = ();
|
|
|
|
fn draw(
|
|
&self,
|
|
_state: &Self::State,
|
|
renderer: &Renderer,
|
|
theme: &Theme,
|
|
bounds: Rectangle,
|
|
_cursor: mouse::Cursor,
|
|
) -> Vec<canvas::Geometry> {
|
|
let mut frame = canvas::Frame::new(renderer, bounds.size());
|
|
|
|
let palette = theme.extended_palette();
|
|
|
|
frame.fill_rectangle(
|
|
Point::ORIGIN,
|
|
bounds.size(),
|
|
palette.background.strong.color,
|
|
);
|
|
|
|
vec![frame.into_geometry()]
|
|
}
|
|
}
|
|
|
|
canvas(Square).width(size).height(size).into()
|
|
}
|