use iced_widget::core::text as core_text;
use iced_widget::core::{Element, Length, alignment};
use iced_widget::text;
use iced_widget::{Column, Container, Row, Scrollable, Text};
use crate::{Theme, text as material_text, tokens};
pub const MAX_WIDTH: f32 = 980.0;
pub const PADDING: f32 = 28.0;
pub const SPACING: f32 = 28.0;
pub const SECTION_SPACING: f32 = 24.0;
pub const STACK_SPACING: f32 = 16.0;
pub const COMPACT_STACK_SPACING: f32 = 8.0;
pub const COMPONENT_STACK_SPACING: f32 = 12.0;
pub const DENSE_STACK_SPACING: f32 = 10.0;
pub const SPACIOUS_STACK_SPACING: f32 = 18.0;
pub const ROW_SPACING: f32 = 12.0;
pub const COMPACT_ROW_SPACING: f32 = 8.0;
pub const INDICATOR_ROW_SPACING: f32 = 16.0;
pub const DIVIDER_ROW_HEIGHT: f32 = 32.0;
pub const DIVIDER_ROW_SPACING: f32 = 16.0;
pub const CARD_PADDING: f32 = 12.0;
pub const CARD_HEIGHT: f32 = 78.0;
pub const PREVIEW_MAX_WIDTH: f32 = tokens::component::bottom_sheet::SHEET_MAX_WIDTH;
pub const PREVIEW_HORIZONTAL_RESERVE: f32 = 160.0;
pub const PREVIEW_MIN_WIDTH: f32 = 320.0;
pub const PREVIEW_HEIGHT: f32 = 260.0;
pub fn surface<'a, Message, Renderer>(
header: impl Into<Element<'a, Message, Theme, Renderer>>,
body: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Scrollable<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
{
let page = Column::new()
.push(header)
.push(body)
.spacing(SPACING)
.padding(PADDING)
.width(Length::Fill)
.max_width(MAX_WIDTH);
Scrollable::new(
super::container::surface_container_high(page)
.width(Length::Fill)
.center_x(Length::Fill),
)
.height(Length::Fill)
}
pub fn preview_width(viewport_width: f32) -> f32 {
(viewport_width - PREVIEW_HORIZONTAL_RESERVE).clamp(PREVIEW_MIN_WIDTH, PREVIEW_MAX_WIDTH)
}
pub fn centered_preview<'a, Message, Renderer>(
width: f32,
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
Container::new(Container::new(content).width(Length::Fixed(width)))
.width(Length::Fill)
.align_x(alignment::Horizontal::Center)
}
pub fn preview_pane<'a, Message, Renderer>(
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
Container::new(content)
.width(Length::Fill)
.height(Length::Fixed(PREVIEW_HEIGHT))
}
pub fn aligned_preview_pane<'a, Message, Renderer>(
alignment: alignment::Horizontal,
content: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
preview_pane(content).align_x(alignment)
}
pub fn header<'a, Message, Renderer>(
title: impl text::IntoFragment<'a>,
subtitle: impl text::IntoFragment<'a>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: core_text::Renderer + 'a,
{
Column::new()
.push(type_scale_text(title, tokens::typography::HEADLINE_LARGE))
.push(type_scale_text(subtitle, tokens::typography::BODY_LARGE))
.spacing(6)
}
pub fn section<'a, Message, Renderer>(
title: impl text::IntoFragment<'a>,
body: impl Into<Element<'a, Message, Theme, Renderer>>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: core_text::Renderer + 'a,
{
Column::new()
.push(type_scale_text(title, tokens::typography::TITLE_MEDIUM))
.push(body)
.spacing(12)
.width(Length::Fill)
}
pub fn sections<'a, Message, Renderer>(
sections: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
{
let mut content = Column::new().spacing(SECTION_SPACING).width(Length::Fill);
let mut first = true;
for section in sections {
if first {
first = false;
} else {
content = content.push(super::rule::horizontal_inset());
}
content = content.push(section);
}
content
}
pub fn stack<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
Column::with_children(children)
.spacing(STACK_SPACING)
.width(Length::Fill)
}
pub fn compact_stack<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
stack(children).spacing(COMPACT_STACK_SPACING)
}
pub fn component_stack<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
stack(children).spacing(COMPONENT_STACK_SPACING)
}
pub fn dense_stack<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
stack(children).spacing(DENSE_STACK_SPACING)
}
pub fn spacious_stack<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Column<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
stack(children).spacing(SPACIOUS_STACK_SPACING)
}
pub fn row<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Row<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
Row::with_children(children)
.spacing(ROW_SPACING)
.align_y(alignment::Vertical::Center)
}
pub fn indicator_row<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Row<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
row(children).spacing(INDICATOR_ROW_SPACING)
}
pub fn compact_row<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Row<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
Row::with_children(children)
.spacing(COMPACT_ROW_SPACING)
.align_y(alignment::Vertical::Center)
}
pub fn labeled_value_row<'a, Message, Renderer>(
label: impl text::IntoFragment<'a>,
value: impl text::IntoFragment<'a>,
) -> Row<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
{
row([
type_scale_text(label, tokens::typography::BODY_LARGE)
.width(Length::Fill)
.into(),
type_scale_text(value, tokens::typography::BODY_LARGE).into(),
])
}
pub fn divider_row<'a, Message, Renderer>(
children: impl IntoIterator<Item = Element<'a, Message, Theme, Renderer>>,
) -> Row<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + 'a,
{
row(children)
.height(Length::Fixed(DIVIDER_ROW_HEIGHT))
.spacing(DIVIDER_ROW_SPACING)
}
pub fn card<'a, Message, Renderer>(
style: fn(Element<'a, Message, Theme, Renderer>) -> Container<'a, Message, Theme, Renderer>,
title: impl text::IntoFragment<'a>,
subtitle: impl text::IntoFragment<'a>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
{
style(
stack([
type_scale_text(title, tokens::typography::TITLE_MEDIUM).into(),
type_scale_text(subtitle, tokens::typography::BODY_MEDIUM).into(),
])
.spacing(2)
.into(),
)
.padding(CARD_PADDING)
.height(Length::Fixed(CARD_HEIGHT))
.width(Length::Fill)
}
pub fn type_scale_text<'a, Renderer>(
content: impl text::IntoFragment<'a>,
scale: tokens::typography::TypeScale,
) -> Text<'a, Theme, Renderer>
where
Renderer: core_text::Renderer + 'a,
{
material_text::type_scale(content, scale)
}
#[cfg(test)]
#[path = "../../../tests/widget/component/page.rs"]
mod tests;