use iced::widget::container; use iced::{Element, Length, Sandbox, Settings}; use numeric_input::numeric_input; pub fn main() -> iced::Result { Component::run(Settings::default()) } #[derive(Default)] struct Component { value: Option, } #[derive(Debug, Clone, Copy)] enum Message { NumericInputChanged(Option), } impl Sandbox for Component { type Message = Message; fn new() -> Self { Self::default() } fn title(&self) -> String { String::from("Component - Iced") } fn update(&mut self, message: Message) { match message { Message::NumericInputChanged(value) => { self.value = value; } } } fn view(&self) -> Element { container(numeric_input(self.value, Message::NumericInputChanged)) .padding(20) .height(Length::Fill) .center_y() .into() } } mod numeric_input { use iced::alignment::{self, Alignment}; use iced::widget::{button, component, row, text, text_input, Component}; use iced::{Element, Length, Renderer}; 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 { 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 { let button = |label, on_press| { button( text(label) .width(Length::Fill) .height(Length::Fill) .horizontal_alignment(alignment::Horizontal::Center) .vertical_alignment(alignment::Vertical::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), ] .align_items(Alignment::Center) .spacing(10) .into() } } impl<'a, Message> From> for Element<'a, Message, Renderer> where Message: 'a, { fn from(numeric_input: NumericInput) -> Self { component(numeric_input) } } }