use crate::data_provider::voice::get_voice; use crate::lang::{CardSet, DictionaryElement, WordOpenMode}; use crate::repetitions::CardSetSettings; use crate::Page::PreviousPage; use crate::{AppState, KeyPressedPage, NavigatedPage, Page, RootMessage, DEFAULT_SPACING}; use iced::alignment::Horizontal::Center; use iced::keyboard::key::Physical::Code; use iced::widget::{button, column, container, row, rule, space, text}; use iced::{alignment, keyboard, Element, Fill, Left, Task}; use rodio::MixerDeviceSink; use std::sync::{Arc, Mutex}; use tokio::task::spawn_blocking; pub struct RepetitionState { pub settings: CardSetSettings, pub set: CardSet, pub state: Arc>, current_word: DictionaryElement, open: bool, can_play: bool, sink: Arc, } impl NavigatedPage for RepetitionState { fn navigate(&self, message: &RepetitionMessage) -> Option { if let RepetitionMessage::Back = message { Some(PreviousPage) } else { None } } } impl RepetitionState { pub(crate) fn new(set: CardSetSettings, state: Arc>) -> RepetitionState { let mut card_set = CardSet::new(&set, state.clone()); let word = card_set.next(); let sink_handle = rodio::DeviceSinkBuilder::open_default_sink().unwrap(); RepetitionState { settings: set, set: card_set, state, current_word: word, open: false, can_play: true, sink: Arc::new(sink_handle) } } } impl RepetitionState { pub fn update(&mut self, message: RepetitionMessage) -> Task { match message { RepetitionMessage::Back => {} RepetitionMessage::Next => return self.next(), RepetitionMessage::Answer(m) => return self.answer(m), RepetitionMessage::Play => { if !self.can_play { return Task::none() } self.can_play = false; let value = self.current_word.key.clone(); return Task::perform(play_sound(self.sink.clone(), value), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished)); }, RepetitionMessage::PlayFinished => { self.can_play = true; } } Task::none() } fn next(&mut self) -> Task { if self.open { self.set.open(WordOpenMode::None); self.current_word = self.set.next(); self.open = false; return Task::perform(play_sound(self.sink.clone(), self.current_word.key.clone()), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished)); } else { self.open = true; return Task::none(); } } fn answer(&mut self, mode: WordOpenMode) -> Task { if !self.open { return Task::none(); } self.set.open(mode); self.open = false; self.current_word = self.set.next(); return Task::perform(play_sound(self.sink.clone(), self.current_word.key.clone()), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished)); } pub fn view(&self) -> Element<'_, RepetitionMessage> { container( iced::widget::column![ button("Назад").on_press(RepetitionMessage::Back), column![ container(self.draw_forward()) .width(Fill) .height(Fill) .align_x(Center) .align_y(alignment::Vertical::Center), rule::horizontal(2), container(self.draw_backward()) .width(Fill) .height(Fill) .align_x(Center) .align_y(alignment::Vertical::Center), container(self.answer_bar()) .width(Fill) .align_x(Center) .height(30) ] .height(Fill) .width(Fill) ] .align_x(Left) .width(Fill), ) .center_x(Fill) .padding(10) .into() } fn draw_forward(&self) -> Element<'_, RepetitionMessage> { let word = &self.current_word; match self.settings.forward.as_str() { "key" => self.draw_key(word), "value" => self.draw_value(word), "speech" => self.draw_voice(), _ => space().into(), } } fn draw_backward(&self) -> Element<'_, RepetitionMessage> { if !self.open { return space().into(); } let word = &self.current_word; match self.settings.backward.as_str() { "key" => self.draw_key(word), "value" => self.draw_value(word), "speech" => self.draw_voice(), _ => space().into(), } } fn answer_bar(&self) -> Element<'_, RepetitionMessage> { if !self.open { return space().into(); } row![ button("Не получилось").on_press(RepetitionMessage::Answer(WordOpenMode::None)), button("Трудно").on_press(RepetitionMessage::Answer(WordOpenMode::Hard)), button("Нормально").on_press(RepetitionMessage::Answer(WordOpenMode::Ok)), button("Легко").on_press(RepetitionMessage::Answer(WordOpenMode::Easy)), ] .spacing(DEFAULT_SPACING) .into() } fn draw_key(&self, word: &DictionaryElement) -> Element<'_, RepetitionMessage> { text!("{}", word.key).size(36).into() } fn draw_value(&self, word: &DictionaryElement) -> Element<'_, RepetitionMessage> { text!("{}", word.value).size(24).into() } fn draw_voice(&self) -> Element<'_, RepetitionMessage> { button("Воспроизвести") .on_press(RepetitionMessage::Play) .into() } } impl KeyPressedPage for RepetitionState { fn press(&mut self, message: &keyboard::Event) -> iced::Task { if let keyboard::Event::KeyPressed { key: _, modified_key: _, physical_key: pk, location: _, modifiers: _, text: _, repeat: _, } = message { if let Code(code) = pk { return match code { keyboard::key::Code::Space => self.next(), keyboard::key::Code::Digit1 => self.answer(WordOpenMode::None), keyboard::key::Code::Digit2 => self.answer(WordOpenMode::Hard), keyboard::key::Code::Digit3 => self.answer(WordOpenMode::Ok), keyboard::key::Code::Digit4 => self.answer(WordOpenMode::Easy), _ => Task::none(), } } } Task::none() } } #[derive(Clone)] pub enum RepetitionMessage { Next, Back, Answer(WordOpenMode), Play, PlayFinished, } async fn play_sound(sink: Arc, text: String) { let data = get_voice(text.as_str()).await; spawn_blocking(move || { rodio::play(&sink.mixer(), data).unwrap().sleep_until_end(); }).await.unwrap(); }