use iced::{ button, scrollable, text::HorizontalAlignment, text_input, Align, Application, Background, Button, Checkbox, Color, Column, Container, Element, Font, Length, Row, Scrollable, Text, TextInput, }; pub fn main() { Todos::default().run() } #[derive(Debug, Default)] struct Todos { scroll: scrollable::State, input: text_input::State, input_value: String, tasks: Vec, } #[derive(Debug, Clone)] pub enum Message { InputChanged(String), CreateTask, TaskMessage(usize, TaskMessage), } impl Application for Todos { type Message = Message; fn title(&self) -> String { String::from("Todos - Iced") } fn update(&mut self, message: Message) { match message { Message::InputChanged(value) => { self.input_value = value; } Message::CreateTask => { if !self.input_value.is_empty() { self.tasks.push(Task::new(self.input_value.clone())); self.input_value.clear(); } } Message::TaskMessage(i, TaskMessage::Delete) => { self.tasks.remove(i); } Message::TaskMessage(i, task_message) => { if let Some(task) = self.tasks.get_mut(i) { task.update(task_message); } } } dbg!(self); } fn view(&mut self) -> Element { let title = Text::new("todos") .size(100) .color(GRAY) .horizontal_alignment(HorizontalAlignment::Center); let input = TextInput::new( &mut self.input, "What needs to be done?", &self.input_value, Message::InputChanged, ) .padding(15) .size(30) .on_submit(Message::CreateTask); let tasks: Element<_> = if self.tasks.len() > 0 { self.tasks .iter_mut() .enumerate() .fold(Column::new().spacing(20), |column, (i, task)| { column.push(task.view().map(move |message| { Message::TaskMessage(i, message) })) }) .into() } else { Container::new( Text::new("You do not have any tasks! :D") .size(25) .horizontal_alignment(HorizontalAlignment::Center) .color([0.7, 0.7, 0.7]), ) .width(Length::Fill) .height(Length::Units(200)) .center_y() .into() }; let content = Column::new() .max_width(800) .spacing(20) .push(title) .push(input) .push(tasks); Scrollable::new(&mut self.scroll) .padding(40) .push(Container::new(content).width(Length::Fill).center_x()) .into() } } #[derive(Debug)] struct Task { description: String, completed: bool, state: TaskState, } #[derive(Debug)] pub enum TaskState { Idle { edit_button: button::State, }, Editing { text_input: text_input::State, delete_button: button::State, }, } #[derive(Debug, Clone)] pub enum TaskMessage { Completed(bool), Edit, DescriptionEdited(String), FinishEdition, Delete, } impl Task { fn new(description: String) -> Self { Task { description, completed: false, state: TaskState::Idle { edit_button: button::State::new(), }, } } fn update(&mut self, message: TaskMessage) { match message { TaskMessage::Completed(completed) => { self.completed = completed; } TaskMessage::Edit => { self.state = TaskState::Editing { text_input: text_input::State::focused(&self.description), delete_button: button::State::new(), }; } TaskMessage::DescriptionEdited(new_description) => { self.description = new_description; } TaskMessage::FinishEdition => { if !self.description.is_empty() { self.state = TaskState::Idle { edit_button: button::State::new(), } } } TaskMessage::Delete => {} } } fn view(&mut self) -> Element { match &mut self.state { TaskState::Idle { edit_button } => { let checkbox = Checkbox::new( self.completed, &self.description, TaskMessage::Completed, ); Row::new() .spacing(20) .align_items(Align::Center) .push(checkbox) .push( Button::new( edit_button, edit_icon().color([0.5, 0.5, 0.5]), ) .on_press(TaskMessage::Edit) .padding(10), ) .into() } TaskState::Editing { text_input, delete_button, } => { let text_input = TextInput::new( text_input, "Describe your task...", &self.description, TaskMessage::DescriptionEdited, ) .on_submit(TaskMessage::FinishEdition) .padding(10); Row::new() .spacing(20) .align_items(Align::Center) .push(text_input) .push( Button::new( delete_button, Row::new() .spacing(10) .push(delete_icon().color(Color::WHITE)) .push( Text::new("Delete") .width(Length::Shrink) .color(Color::WHITE), ), ) .on_press(TaskMessage::Delete) .padding(10) .border_radius(5) .background(Background::Color([0.8, 0.2, 0.2].into())), ) .into() } } } } // Colors const GRAY: Color = Color { r: 0.5, g: 0.5, b: 0.5, a: 1.0, }; // Fonts const ICONS: Font = Font::External { name: "Icons", bytes: include_bytes!("./resources/icons.ttf"), }; fn icon(unicode: char) -> Text { Text::new(&unicode.to_string()) .font(ICONS) .width(Length::Units(20)) .horizontal_alignment(HorizontalAlignment::Center) .size(20) } fn edit_icon() -> Text { icon('\u{F303}') } fn delete_icon() -> Text { icon('\u{F1F8}') }