use iced_widget::checkbox as iced_checkbox;
use iced_widget::container as iced_container;
use iced_widget::core::svg as core_svg;
use iced_widget::core::text as core_text;
use iced_widget::core::time::Instant;
use iced_widget::core::widget as core_widget;
use iced_widget::core::widget::tree::{self, Tree};
use iced_widget::core::{
Background, Border, Clipboard, Color, Element, Event, Layout, Length, Padding, Pixels, Point,
Rectangle, Shell, Size, Vector, Widget, alignment, border, input_method, layout, mouse,
overlay, renderer, touch, window,
};
use iced_widget::radio as iced_radio;
use iced_widget::rule as iced_rule;
use iced_widget::text::{self, LineHeight};
use iced_widget::text_editor as iced_text_editor;
use iced_widget::text_input as iced_text_input;
use iced_widget::toggler as iced_toggler;
use iced_widget::tooltip as iced_tooltip;
use iced_widget::{
Container, Row, Rule, Text, TextEditor as IcedTextEditor, TextInput as IcedTextInput,
};
use crate::style::{
button as button_style, checkbox as checkbox_style, container as container_style,
rule as rule_style, slider as slider_style, text_editor as text_editor_style,
text_input as text_input_style, toggler as toggler_style, tooltip as tooltip_style,
};
use crate::utils::mix;
use crate::{Theme, fonts, tokens, web_input};
#[path = "widget/component/app_bar.rs"]
pub mod app_bar;
#[path = "widget/component/badge.rs"]
pub mod badge;
#[path = "widget/component/card.rs"]
pub mod card;
#[path = "widget/component/combobox.rs"]
pub mod combobox;
#[path = "widget/component/data_table.rs"]
pub mod data_table;
#[path = "widget/component/dialog.rs"]
pub mod dialog;
#[path = "widget/component/list.rs"]
pub mod list;
#[path = "widget/internal/menu_overlay.rs"]
mod menu_overlay;
#[path = "widget/component/navigation.rs"]
pub mod navigation;
#[path = "widget/component/page.rs"]
pub mod page;
#[path = "widget/component/picker.rs"]
pub mod picker;
#[path = "widget/component/progress_bar.rs"]
pub mod progress_bar;
#[path = "widget/internal/ripple.rs"]
mod ripple;
#[path = "widget/component/search.rs"]
pub mod search;
#[path = "widget/component/segmented_button.rs"]
pub mod segmented_button;
#[path = "widget/component/select.rs"]
pub mod select;
#[path = "widget/component/sheet.rs"]
pub mod sheet;
#[path = "widget/component/snackbar.rs"]
pub mod snackbar;
#[path = "widget/internal/support.rs"]
mod support;
#[path = "widget/component/tabs.rs"]
pub mod tabs;
#[path = "widget/component/theme_picker.rs"]
pub mod theme_picker;
#[path = "widget/component/toolbar.rs"]
pub mod toolbar;
#[path = "widget/component/viewport.rs"]
pub mod viewport;
use support::{
AnimatedScalar, SelectionState, TextFieldState, TextFieldTouchActivation, alpha_border,
alpha_color, bool_value, draw_text_field_notched, draw_text_field_outline, duration_ms, lerp,
scaled_rect, solid_color, text_field_floating_label_notch,
};
const TEXT_FIELD_TOUCH_SLOP: f32 = 8.0;
const SWITCH_ON_ICON_SVG: &[u8] = br##"
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M9.55 18.2 3.65 12.3 5.275 10.675 9.55 14.95 18.725 5.775 20.35 7.4Z"/>
</svg>
"##;
const SWITCH_OFF_ICON_SVG: &[u8] = br##"
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M6.4 19.2 4.8 17.6 10.4 12 4.8 6.4 6.4 4.8 12 10.4 17.6 4.8 19.2 6.4 13.6 12 19.2 17.6 17.6 19.2 12 13.6Z"/>
</svg>
"##;
fn absolute_line_height(value: f32) -> LineHeight {
LineHeight::Absolute(value.into())
}
#[cfg(target_os = "windows")]
fn normalize_windows_ime_request(
input_method: &mut input_method::InputMethod,
avoid_bounds: Rectangle,
) {
let input_method::InputMethod::Enabled {
cursor, preedit, ..
} = input_method
else {
return;
};
if !preedit
.as_ref()
.is_some_and(|preedit| !preedit.content.is_empty())
{
return;
}
*preedit = None;
let bounds_right = avoid_bounds.x + avoid_bounds.width;
let bounds_bottom = avoid_bounds.y + avoid_bounds.height;
let cursor_right = cursor.x + cursor.width;
let cursor_bottom = cursor.y + cursor.height;
if cursor.x < bounds_right
&& cursor_right > avoid_bounds.x
&& cursor.y < bounds_bottom
&& cursor_bottom > avoid_bounds.y
{
cursor.x = avoid_bounds.x;
cursor.width = avoid_bounds.width;
cursor.height = (bounds_bottom - cursor.y).max(cursor.height);
}
}
#[cfg(not(target_os = "windows"))]
fn normalize_windows_ime_request(
_input_method: &mut input_method::InputMethod,
_avoid_bounds: Rectangle,
) {
}
fn text_with_metrics<'a, Renderer>(
content: impl text::IntoFragment<'a>,
size: f32,
line_height: f32,
) -> Text<'a, Theme, Renderer>
where
Renderer: core_text::Renderer,
{
Text::new(content)
.size(size)
.line_height(absolute_line_height(line_height))
}
fn centered_icon_text<'a, Renderer>(
icon: impl text::IntoFragment<'a>,
size: f32,
) -> Text<'a, Theme, Renderer>
where
Renderer: core_text::Renderer,
iced_widget::core::Font: Into<Renderer::Font>,
{
fonts::icon(icon, size)
.width(Length::Fixed(size))
.height(Length::Fixed(size))
.center()
}
fn text_field_touch_cursor(event: &Event, cursor: mouse::Cursor) -> mouse::Cursor {
match event {
Event::Touch(
touch::Event::FingerPressed { position, .. }
| touch::Event::FingerMoved { position, .. }
| touch::Event::FingerLifted { position, .. }
| touch::Event::FingerLost { position, .. },
) if cursor.position().is_none() && !cursor.is_levitating() => {
mouse::Cursor::Available(*position)
}
_ => cursor,
}
}
fn touch_as_mouse_event(event: &Event) -> Option<Event> {
match event {
Event::Touch(touch::Event::FingerPressed { .. }) => Some(Event::Mouse(
mouse::Event::ButtonPressed(mouse::Button::Left),
)),
Event::Touch(touch::Event::FingerMoved { position, .. }) => {
Some(Event::Mouse(mouse::Event::CursorMoved {
position: *position,
}))
}
Event::Touch(touch::Event::FingerLifted { .. } | touch::Event::FingerLost { .. }) => Some(
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)),
),
_ => None,
}
}
fn text_field_touch_position(position: Point, cursor: mouse::Cursor) -> Option<Point> {
if let Some(cursor_position) = cursor.position() {
return Some(cursor_position);
}
if cursor.is_levitating() {
return None;
}
Some(position)
}
fn text_field_touch_press_is_over(event: &Event, bounds: Rectangle, cursor: mouse::Cursor) -> bool {
matches!(
event,
Event::Touch(touch::Event::FingerPressed { position, .. })
if text_field_touch_position(*position, cursor).is_some_and(|position| bounds.contains(position))
)
}
fn text_field_keyboard_activation(
touch_activation: &mut Option<TextFieldTouchActivation>,
event: &Event,
bounds: Rectangle,
cursor: mouse::Cursor,
) -> bool {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => cursor.is_over(bounds),
Event::Touch(touch::Event::FingerPressed { id, position }) => {
if let Some(position) = text_field_touch_position(*position, cursor)
&& bounds.contains(position)
{
*touch_activation = Some(TextFieldTouchActivation::new(*id, position));
} else {
*touch_activation = None;
}
false
}
Event::Touch(touch::Event::FingerMoved { id, position }) => {
if let Some(position) = text_field_touch_position(*position, cursor) {
if touch_activation.is_some_and(|activation| {
activation.matches(*id)
&& activation.moved_beyond_slop(position, TEXT_FIELD_TOUCH_SLOP)
}) {
*touch_activation = None;
}
}
false
}
Event::Touch(touch::Event::FingerLifted { id, position }) => {
let position = text_field_touch_position(*position, cursor);
touch_activation.take().is_some_and(|activation| {
position
.is_some_and(|position| activation.matches(*id) && bounds.contains(position))
})
}
Event::Touch(touch::Event::FingerLost { id, .. }) => {
if touch_activation.is_some_and(|activation| activation.matches(*id)) {
*touch_activation = None;
}
false
}
_ => false,
}
}
fn text_field_visible_keyboard_activation(
touch_activation: &mut Option<TextFieldTouchActivation>,
event: &Event,
visible_bounds: Option<Rectangle>,
cursor: mouse::Cursor,
) -> bool {
let Some(bounds) = visible_bounds else {
if matches!(event, Event::Touch(_)) {
*touch_activation = None;
}
return false;
};
text_field_keyboard_activation(touch_activation, event, bounds, cursor)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum TextFieldInnerTouchHandling {
Forward,
Suppress,
ConfirmedTap,
}
#[derive(Debug, Clone, Copy)]
struct TextInputActivation {
cursor: mouse::Cursor,
request_mobile_keyboard: bool,
inner_touch_handling: TextFieldInnerTouchHandling,
}
#[derive(Debug, Clone, Copy, Default)]
struct MobileTextInputState {
touch_activation: Option<TextFieldTouchActivation>,
}
fn mobile_text_input_activation(
is_enabled: bool,
state: &mut MobileTextInputState,
event: &Event,
visible_bounds: Option<Rectangle>,
cursor: mouse::Cursor,
) -> TextInputActivation {
text_input_activation(
is_enabled,
&mut state.touch_activation,
event,
visible_bounds,
cursor,
)
}
fn text_input_activation(
is_enabled: bool,
touch_activation: &mut Option<TextFieldTouchActivation>,
event: &Event,
visible_bounds: Option<Rectangle>,
cursor: mouse::Cursor,
) -> TextInputActivation {
let inner_cursor = text_field_touch_cursor(event, cursor);
let touch_activation_before = *touch_activation;
let request_mobile_keyboard = is_enabled
&& text_field_visible_keyboard_activation(
touch_activation,
event,
visible_bounds,
inner_cursor,
);
let inner_touch_handling = text_field_inner_touch_handling_for_visible_bounds(
is_enabled,
event,
visible_bounds,
inner_cursor,
touch_activation_before,
request_mobile_keyboard,
);
TextInputActivation {
cursor: inner_cursor,
request_mobile_keyboard,
inner_touch_handling,
}
}
fn sync_mobile_keyboard(started_focused: bool, is_focused: bool, request_mobile_keyboard: bool) {
if started_focused != is_focused {
if is_focused {
if !request_mobile_keyboard {
web_input::show_mobile_keyboard();
}
} else {
web_input::hide_mobile_keyboard();
}
}
if request_mobile_keyboard {
web_input::show_mobile_keyboard();
}
}
fn register_mobile_text_region(is_enabled: bool, bounds: Rectangle, viewport: &Rectangle) {
if is_enabled && let Some(visible_bounds) = bounds.intersection(viewport) {
web_input::register_text_region(visible_bounds);
}
}
#[expect(clippy::too_many_arguments)]
fn update_mobile_text_input<'a, Message, Renderer>(
input: &mut IcedTextInput<'a, Message, Theme, Renderer>,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
activation: TextInputActivation,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) where
Message: Clone,
Renderer: iced_widget::core::Renderer + core_text::Renderer,
{
match activation.inner_touch_handling {
TextFieldInnerTouchHandling::Forward => {
input.update(
tree,
event,
layout,
activation.cursor,
renderer,
clipboard,
shell,
viewport,
);
}
TextFieldInnerTouchHandling::Suppress => {}
TextFieldInnerTouchHandling::ConfirmedTap => {
let press = Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left));
input.update(
tree,
&press,
layout,
activation.cursor,
renderer,
clipboard,
shell,
viewport,
);
let release = Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left));
input.update(
tree,
&release,
layout,
activation.cursor,
renderer,
clipboard,
shell,
viewport,
);
}
}
refresh_text_input_caret(
tree.state
.downcast_mut::<iced_text_input::State<Renderer::Paragraph>>(),
event,
shell,
);
}
fn text_field_touch_matches_activation(
touch_activation: Option<TextFieldTouchActivation>,
event: &Event,
) -> bool {
let Some(activation) = touch_activation else {
return false;
};
match event {
Event::Touch(
touch::Event::FingerMoved { id, .. }
| touch::Event::FingerLifted { id, .. }
| touch::Event::FingerLost { id, .. },
) => activation.matches(*id),
_ => false,
}
}
fn text_field_inner_touch_handling(
is_enabled: bool,
event: &Event,
bounds: Rectangle,
cursor: mouse::Cursor,
touch_activation_before: Option<TextFieldTouchActivation>,
confirmed_tap: bool,
) -> TextFieldInnerTouchHandling {
if !is_enabled {
return TextFieldInnerTouchHandling::Forward;
}
if confirmed_tap {
return TextFieldInnerTouchHandling::ConfirmedTap;
}
if text_field_touch_press_is_over(event, bounds, cursor)
|| text_field_touch_matches_activation(touch_activation_before, event)
{
return TextFieldInnerTouchHandling::Suppress;
}
TextFieldInnerTouchHandling::Forward
}
fn text_field_inner_touch_handling_for_visible_bounds(
is_enabled: bool,
event: &Event,
visible_bounds: Option<Rectangle>,
cursor: mouse::Cursor,
touch_activation_before: Option<TextFieldTouchActivation>,
confirmed_tap: bool,
) -> TextFieldInnerTouchHandling {
let Some(bounds) = visible_bounds else {
return if matches!(event, Event::Touch(_)) {
TextFieldInnerTouchHandling::Suppress
} else {
TextFieldInnerTouchHandling::Forward
};
};
text_field_inner_touch_handling(
is_enabled,
event,
bounds,
cursor,
touch_activation_before,
confirmed_tap,
)
}
fn press_is_over(event: &Event, bounds: Rectangle, cursor: mouse::Cursor) -> bool {
match event {
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => cursor.is_over(bounds),
Event::Touch(touch::Event::FingerPressed { position, .. }) => {
touch_event_is_over(*position, bounds, cursor)
}
_ => false,
}
}
fn release_is_over(event: &Event, bounds: Rectangle, cursor: mouse::Cursor) -> bool {
match event {
Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)) => cursor.is_over(bounds),
Event::Touch(touch::Event::FingerLifted { position, .. }) => {
touch_event_is_over(*position, bounds, cursor)
}
_ => false,
}
}
fn touch_event_is_over(position: Point, bounds: Rectangle, cursor: mouse::Cursor) -> bool {
if cursor.position().is_some() {
return cursor.is_over(bounds);
}
if cursor.is_levitating() {
return false;
}
bounds.contains(position)
}
fn selection_control_hit_bounds(layout: Layout<'_>, target_size: f32) -> Rectangle {
let content_bounds = layout.bounds();
let control_bounds = layout
.children()
.next()
.map_or(content_bounds, |control| control.bounds());
selection_control_hit_bounds_from_rects(content_bounds, control_bounds, target_size)
}
fn selection_control_hit_bounds_from_rects(
content_bounds: Rectangle,
control_bounds: Rectangle,
target_size: f32,
) -> Rectangle {
let target_height = content_bounds.height.max(target_size);
let content_target = Rectangle {
y: content_bounds.center_y() - target_height / 2.0,
height: target_height,
..content_bounds
};
let control_padding =
((target_size - control_bounds.width.min(control_bounds.height)) / 2.0).max(0.0);
let control_target = Rectangle {
x: control_bounds.x - control_padding,
y: control_bounds.y - control_padding,
width: control_bounds.width + control_padding * 2.0,
height: control_bounds.height + control_padding * 2.0,
};
union_bounds(content_target, control_target)
}
fn union_bounds(a: Rectangle, b: Rectangle) -> Rectangle {
let x = a.x.min(b.x);
let y = a.y.min(b.y);
let right = (a.x + a.width).max(b.x + b.width);
let bottom = (a.y + a.height).max(b.y + b.height);
Rectangle {
x,
y,
width: right - x,
height: bottom - y,
}
}
fn should_suppress_ime_caret() -> bool {
!cfg!(any(
target_arch = "wasm32",
target_os = "android",
target_os = "windows"
))
}
fn text_caret_refresh_event(event: &Event) -> bool {
match event {
Event::Keyboard(iced_widget::core::keyboard::Event::KeyPressed { key, text, .. }) => {
text.as_ref()
.is_some_and(|text| text.chars().any(|c| !c.is_control()))
|| matches!(
key.as_ref(),
iced_widget::core::keyboard::Key::Named(
iced_widget::core::keyboard::key::Named::Enter
| iced_widget::core::keyboard::key::Named::Backspace
| iced_widget::core::keyboard::key::Named::Delete
)
)
}
Event::InputMethod(input_method::Event::Preedit(content, _)) => !content.is_empty(),
Event::InputMethod(input_method::Event::Commit(content)) => !content.is_empty(),
_ => false,
}
}
fn refresh_text_input_caret<Message, P>(
state: &mut iced_text_input::State<P>,
event: &Event,
shell: &mut Shell<'_, Message>,
) where
P: core_text::Paragraph,
{
if !state.is_focused() || !text_caret_refresh_event(event) {
return;
}
let value = {
let text = <iced_text_input::State<P> as core_widget::operation::TextInput>::text(state);
iced_text_input::Value::new(text)
};
let cursor = state.cursor().state(&value);
core_widget::operation::Focusable::focus(state);
match cursor {
iced_text_input::cursor::State::Index(index) => {
state.move_cursor_to(index);
}
iced_text_input::cursor::State::Selection { start, end } => {
state.select_range(start, end);
}
}
shell.request_redraw();
}
fn mobile_text_input<'a, Message, Renderer>(
input: IcedTextInput<'a, Message, Theme, Renderer>,
is_enabled: bool,
) -> Element<'a, Message, Theme, Renderer>
where
Message: Clone + 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
{
Element::new(MobileTextInput { input, is_enabled })
}
struct MobileTextInput<'a, Message, Renderer>
where
Renderer: iced_widget::core::Renderer + core_text::Renderer,
{
input: IcedTextInput<'a, Message, Theme, Renderer>,
is_enabled: bool,
}
impl<Message, Renderer> Widget<Message, Theme, Renderer> for MobileTextInput<'_, Message, Renderer>
where
Message: Clone,
Renderer: iced_widget::core::Renderer + core_text::Renderer,
{
fn tag(&self) -> tree::Tag {
tree::Tag::of::<MobileTextInputState>()
}
fn state(&self) -> tree::State {
tree::State::new(MobileTextInputState::default())
}
fn children(&self) -> Vec<Tree> {
let input: &dyn Widget<Message, Theme, Renderer> = &self.input;
vec![Tree::new(input)]
}
fn diff(&self, tree: &mut Tree) {
if tree.children.is_empty() {
tree.children = self.children();
} else {
self.input.diff(&mut tree.children[0]);
tree.children.truncate(1);
}
}
fn size(&self) -> Size<Length> {
Widget::<Message, Theme, Renderer>::size(&self.input)
}
fn size_hint(&self) -> Size<Length> {
Widget::<Message, Theme, Renderer>::size_hint(&self.input)
}
fn layout(
&mut self,
tree: &mut Tree,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let input = <IcedTextInput<'_, Message, Theme, Renderer> as Widget<
Message,
Theme,
Renderer,
>>::layout(&mut self.input, &mut tree.children[0], renderer, limits);
layout::Node::with_children(input.size(), vec![input])
}
fn operate(
&mut self,
tree: &mut Tree,
layout: Layout<'_>,
renderer: &Renderer,
operation: &mut dyn core_widget::Operation,
) {
self.input.operate(
&mut tree.children[0],
layout.children().next().unwrap(),
renderer,
operation,
);
}
fn update(
&mut self,
tree: &mut Tree,
event: &Event,
layout: Layout<'_>,
cursor: mouse::Cursor,
renderer: &Renderer,
clipboard: &mut dyn Clipboard,
shell: &mut Shell<'_, Message>,
viewport: &Rectangle,
) {
let bounds = layout.bounds();
let visible_bounds = bounds.intersection(viewport);
let input_layout = layout.children().next().unwrap();
let started_focused = {
let state = tree.children[0]
.state
.downcast_ref::<iced_text_input::State<Renderer::Paragraph>>();
state.is_focused()
};
let activation = mobile_text_input_activation(
self.is_enabled,
tree.state.downcast_mut::<MobileTextInputState>(),
event,
visible_bounds,
cursor,
);
update_mobile_text_input(
&mut self.input,
&mut tree.children[0],
event,
input_layout,
activation,
renderer,
clipboard,
shell,
viewport,
);
normalize_windows_ime_request(shell.input_method_mut(), bounds);
let is_focused = {
let state = tree.children[0]
.state
.downcast_ref::<iced_text_input::State<Renderer::Paragraph>>();
state.is_focused()
};
sync_mobile_keyboard(
started_focused,
is_focused,
activation.request_mobile_keyboard,
);
}
fn mouse_interaction(
&self,
tree: &Tree,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
renderer: &Renderer,
) -> mouse::Interaction {
self.input.mouse_interaction(
&tree.children[0],
layout.children().next().unwrap(),
cursor,
viewport,
renderer,
)
}
fn draw(
&self,
tree: &Tree,
renderer: &mut Renderer,
theme: &Theme,
defaults: &renderer::Style,
layout: Layout<'_>,
cursor: mouse::Cursor,
viewport: &Rectangle,
) {
register_mobile_text_region(self.is_enabled, layout.bounds(), viewport);
<IcedTextInput<'_, Message, Theme, Renderer> as Widget<Message, Theme, Renderer>>::draw(
&self.input,
&tree.children[0],
renderer,
theme,
defaults,
layout.children().next().unwrap(),
cursor,
viewport,
);
}
}
fn checkbox_checkmark_svg(mark_progress: f32) -> Vec<u8> {
let progress = mark_progress.clamp(0.0, 1.0);
let short_height = lerp(
tokens::component::checkbox::CHECKMARK_STROKE_WIDTH,
tokens::component::checkbox::CHECKMARK_SHORT_MARK_SIZE,
progress,
);
let long_width = tokens::component::checkbox::CHECKMARK_LONG_MARK_SIZE * progress;
format!(
r#"<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><g transform="scale(1 -1) translate({} {}) rotate(45)"><rect width="{}" height="{short_height}"/><rect width="{long_width}" height="{}"/></g></svg>"#,
tokens::component::checkbox::CHECKMARK_BOTTOM_LEFT_X,
tokens::component::checkbox::CHECKMARK_BOTTOM_LEFT_Y,
tokens::component::checkbox::CHECKMARK_STROKE_WIDTH,
tokens::component::checkbox::CHECKMARK_STROKE_WIDTH,
)
.into_bytes()
}
fn text_button_content<'a, Message, Renderer>(
label: impl text::IntoFragment<'a>,
label_size: f32,
label_line_height: f32,
height: f32,
horizontal_padding: f32,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
{
Container::new(text_with_metrics(label, label_size, label_line_height))
.height(Length::Fixed(height))
.padding(Padding::from([0.0, horizontal_padding]))
.align_y(alignment::Vertical::Center)
}
fn icon_button_content<'a, Message, Renderer>(
icon: impl text::IntoFragment<'a>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
iced_widget::core::Font: Into<Renderer::Font>,
{
let icon = centered_icon_text(icon, tokens::component::icon_button::ICON_SIZE);
Container::new(icon)
.center_x(Length::Fixed(
tokens::component::icon_button::CONTAINER_WIDTH,
))
.center_y(Length::Fixed(
tokens::component::icon_button::CONTAINER_HEIGHT,
))
}
fn fab_content<'a, Message, Renderer>(
icon: impl text::IntoFragment<'a>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
iced_widget::core::Font: Into<Renderer::Font>,
{
sized_fab_content(
icon,
tokens::component::fab::CONTAINER_WIDTH,
tokens::component::fab::CONTAINER_HEIGHT,
tokens::component::fab::ICON_SIZE,
)
}
fn sized_fab_content<'a, Message, Renderer>(
icon: impl text::IntoFragment<'a>,
width: f32,
height: f32,
icon_size: f32,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
iced_widget::core::Font: Into<Renderer::Font>,
{
let icon = centered_icon_text(icon, icon_size);
Container::new(icon)
.center_x(Length::Fixed(width))
.center_y(Length::Fixed(height))
}
fn extended_fab_content<'a, Message, Renderer>(
label: impl text::IntoFragment<'a>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
{
let label_text = tokens::component::fab::EXTENDED_LABEL_TEXT;
Container::new(text_with_metrics(
label,
label_text.size,
label_text.line_height,
))
.height(Length::Fixed(
tokens::component::fab::EXTENDED_CONTAINER_HEIGHT,
))
.padding(Padding {
top: 0.0,
right: tokens::component::fab::EXTENDED_TRAILING_SPACE,
bottom: 0.0,
left: tokens::component::fab::EXTENDED_LEADING_SPACE,
})
.align_y(alignment::Vertical::Center)
}
fn extended_fab_icon_content<'a, Message, Renderer>(
icon: impl text::IntoFragment<'a>,
label: impl text::IntoFragment<'a>,
) -> Container<'a, Message, Theme, Renderer>
where
Message: 'a,
Renderer: iced_widget::core::Renderer + core_text::Renderer + 'a,
iced_widget::core::Font: Into<Renderer::Font>,
{
let label_text = tokens::component::fab::EXTENDED_LABEL_TEXT;
let content = Row::new()
.push(centered_icon_text(
icon,
tokens::component::fab::EXTENDED_ICON_SIZE,
))
.push(text_with_metrics(
label,
label_text.size,
label_text.line_height,
))
.spacing(tokens::component::fab::EXTENDED_ICON_LABEL_SPACE)
.align_y(alignment::Vertical::Center);
Container::new(content)
.height(Length::Fixed(
tokens::component::fab::EXTENDED_CONTAINER_HEIGHT,
))
.padding(Padding {
top: 0.0,
right: tokens::component::fab::EXTENDED_TRAILING_SPACE,
bottom: 0.0,
left: tokens::component::fab::EXTENDED_LEADING_SPACE,
})
.align_y(alignment::Vertical::Center)
}
#[path = "widget/component/button.rs"]
pub mod button;
#[path = "widget/component/slider.rs"]
pub mod slider;
#[path = "widget/component/rule.rs"]
pub mod rule;
#[path = "widget/component/container.rs"]
pub mod container;
#[path = "widget/component/text_input.rs"]
pub mod text_input;
#[path = "widget/component/tooltip.rs"]
pub mod tooltip;
#[path = "widget/component/text_editor.rs"]
pub mod text_editor;
#[path = "widget/component/radio.rs"]
pub mod radio;
#[path = "widget/component/checkbox.rs"]
pub mod checkbox;
#[path = "widget/component/toggler.rs"]
pub mod toggler;
#[cfg(test)]
#[path = "../tests/widget.rs"]
mod tests;