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<u32>,
}
#[derive(Debug, Clone, Copy)]
enum Message {
NumericInputChanged(Option<u32>),
}
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<Message> {
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<Message> {
value: Option<u32>,
on_change: Box<dyn Fn(Option<u32>) -> Message>,
}
pub fn numeric_input<Message>(
value: Option<u32>,
on_change: impl Fn(Option<u32>) -> Message + 'static,
) -> NumericInput<Message> {
NumericInput::new(value, on_change)
}
#[derive(Debug, Clone)]
pub enum Event {
InputChanged(String),
IncrementPressed,
DecrementPressed,
}
impl<Message> NumericInput<Message> {
pub fn new(
value: Option<u32>,
on_change: impl Fn(Option<u32>) -> Message + 'static,
) -> Self {
Self {
value,
on_change: Box::new(on_change),
}
}
}
impl<Message> Component<Message, Renderer> for NumericInput<Message> {
type State = ();
type Event = Event;
fn update(
&mut self,
_state: &mut Self::State,
event: Event,
) -> Option<Message> {
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, Renderer> {
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<NumericInput<Message>> for Element<'a, Message, Renderer>
where
Message: 'a,
{
fn from(numeric_input: NumericInput<Message>) -> Self {
component(numeric_input)
}
}
}