Draft component example 🎉

This commit is contained in:
Héctor Ramón Jiménez 2021-11-08 15:29:58 +07:00
parent bffa7203df
commit 010b62b9ee
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
4 changed files with 200 additions and 5 deletions

View file

@ -66,6 +66,7 @@ members = [
"examples/bezier_tool",
"examples/clock",
"examples/color_palette",
"examples/component",
"examples/counter",
"examples/custom_widget",
"examples/download_progress",

View file

@ -0,0 +1,11 @@
[package]
name = "component"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["debug"] }
iced_native = { path = "../../native" }
iced_lazy = { path = "../../lazy" }

View file

@ -0,0 +1,180 @@
use iced::{Container, Element, Length, Sandbox, Settings};
use numeric_input::NumericInput;
pub fn main() -> iced::Result {
Component::run(Settings::default())
}
#[derive(Default)]
struct Component {
numeric_input: numeric_input::State,
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(&mut self) -> Element<Message> {
Container::new(NumericInput::new(
&mut self.numeric_input,
self.value,
Message::NumericInputChanged,
))
.padding(20)
.height(Length::Fill)
.center_y()
.into()
}
}
mod numeric_input {
use iced_lazy::component::{self, Component};
use iced_native::alignment::{self, Alignment};
use iced_native::text;
use iced_native::widget::button::{self, Button};
use iced_native::widget::text_input::{self, TextInput};
use iced_native::widget::{Row, Text};
use iced_native::{Element, Length};
pub struct NumericInput<'a, Message> {
state: &'a mut State,
value: Option<u32>,
on_change: Box<dyn Fn(Option<u32>) -> Message>,
}
#[derive(Default)]
pub struct State {
input: text_input::State,
decrement_button: button::State,
increment_button: button::State,
}
#[derive(Debug, Clone)]
pub enum Event {
InputChanged(String),
IncrementPressed,
DecrementPressed,
}
impl<'a, Message> NumericInput<'a, Message> {
pub fn new(
state: &'a mut State,
value: Option<u32>,
on_change: impl Fn(Option<u32>) -> Message + 'static,
) -> Self {
Self {
state,
value,
on_change: Box::new(on_change),
}
}
}
impl<'a, Message, Renderer> Component<Message, Renderer>
for NumericInput<'a, Message>
where
Renderer: 'a + text::Renderer,
{
type Event = Event;
fn update(&mut self, 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(&mut self) -> Element<Event, Renderer> {
let button = |state, label, on_press| {
Button::new(
state,
Text::new(label)
.width(Length::Fill)
.height(Length::Fill)
.horizontal_alignment(alignment::Horizontal::Center)
.vertical_alignment(alignment::Vertical::Center),
)
.width(Length::Units(50))
.on_press(on_press)
};
Row::with_children(vec![
button(
&mut self.state.decrement_button,
"-",
Event::DecrementPressed,
)
.into(),
TextInput::new(
&mut self.state.input,
"Type a number",
self.value
.as_ref()
.map(u32::to_string)
.as_ref()
.map(String::as_str)
.unwrap_or(""),
Event::InputChanged,
)
.padding(10)
.into(),
button(
&mut self.state.increment_button,
"+",
Event::IncrementPressed,
)
.into(),
])
.align_items(Alignment::Fill)
.spacing(10)
.into()
}
}
impl<'a, Message, Renderer> From<NumericInput<'a, Message>>
for Element<'a, Message, Renderer>
where
Message: 'a,
Renderer: text::Renderer + 'a,
{
fn from(numeric_input: NumericInput<'a, Message>) -> Self {
component::view(Box::new(numeric_input))
}
}
}

View file

@ -11,10 +11,12 @@ use ouroboros::self_referencing;
use std::marker::PhantomData;
pub fn view<'a, Event, Message, Renderer>(
component: &'a mut dyn Component<Message, Renderer, Event = Event>,
component: Box<dyn Component<Message, Renderer, Event = Event> + 'a>,
) -> Element<'a, Message, Renderer>
where
Renderer: iced_native::Renderer,
Message: 'a,
Event: 'a,
Renderer: iced_native::Renderer + 'a,
{
Element::new(Instance {
state: Some(
@ -43,8 +45,8 @@ struct Instance<'a, Message, Renderer, Event> {
}
#[self_referencing]
struct State<'a, Message, Renderer, Event> {
component: &'a mut dyn Component<Message, Renderer, Event = Event>,
struct State<'a, Message: 'a, Renderer: 'a, Event: 'a> {
component: Box<dyn Component<Message, Renderer, Event = Event> + 'a>,
#[borrows(mut component)]
#[covariant]
@ -106,7 +108,8 @@ where
});
if !local_messages.is_empty() {
let component = self.state.take().unwrap().into_heads().component;
let mut component =
self.state.take().unwrap().into_heads().component;
messages.extend(
local_messages