use iced::widget::center; use iced::Element; use numeric_input::numeric_input; pub fn main() -> iced::Result { iced::run("Component - Iced", Component::update, Component::view) } #[derive(Default)] struct Component { value: Option, } #[derive(Debug, Clone, Copy)] enum Message { NumericInputChanged(Option), } impl Component { fn update(&mut self, message: Message) { match message { Message::NumericInputChanged(value) => { self.value = value; } } } fn view(&self) -> Element { center(numeric_input(self.value, Message::NumericInputChanged)) .padding(20) .into() } } mod numeric_input { use iced::widget::{button, component, row, text, text_input, Component}; use iced::{Element, Length, Size}; pub struct NumericInput { value: Option, on_change: Box) -> Message>, } pub fn numeric_input( value: Option, on_change: impl Fn(Option) -> Message + 'static, ) -> NumericInput { NumericInput::new(value, on_change) } #[derive(Debug, Clone)] pub enum Event { InputChanged(String), IncrementPressed, DecrementPressed, } impl NumericInput { pub fn new( value: Option, on_change: impl Fn(Option) -> Message + 'static, ) -> Self { Self { value, on_change: Box::new(on_change), } } } impl Component for NumericInput where Theme: text::Catalog + button::Catalog + text_input::Catalog + 'static, { type State = (); type Event = Event; fn update( &mut self, _state: &mut Self::State, event: Event, ) -> Option { match event { Event::IncrementPressed => Some((self.on_change)(Some( self.value.unwrap_or_default().saturating_add(1), ))), Event::DecrementPressed => Some((self.on_change)(Some( self.value.unwrap_or_default().saturating_sub(1), ))), Event::InputChanged(value) => { if value.is_empty() { Some((self.on_change)(None)) } else { value .parse() .ok() .map(Some) .map(self.on_change.as_ref()) } } } } fn view(&self, _state: &Self::State) -> Element<'_, Event, Theme> { let button = |label, on_press| { button( text(label) .width(Length::Fill) .height(Length::Fill) .center(), ) .width(40) .height(40) .on_press(on_press) }; row![ button("-", Event::DecrementPressed), text_input( "Type a number", self.value .as_ref() .map(u32::to_string) .as_deref() .unwrap_or(""), ) .on_input(Event::InputChanged) .padding(10), button("+", Event::IncrementPressed), ] .center_y() .spacing(10) .into() } fn size_hint(&self) -> Size { Size { width: Length::Fill, height: Length::Shrink, } } } impl<'a, Message, Theme> From> for Element<'a, Message, Theme> where Theme: text::Catalog + button::Catalog + text_input::Catalog + 'static, Message: 'a, { fn from(numeric_input: NumericInput) -> Self { component(numeric_input) } } }