Files
jap_learn/src/repetition.rs
T

232 lines
7.3 KiB
Rust
Raw Normal View History

2026-04-19 22:56:58 +03:00
use crate::data_provider::voice::get_voice;
2026-04-14 15:19:46 +03:00
use crate::lang::{CardSet, DictionaryElement, WordOpenMode};
2026-04-13 22:25:58 +03:00
use crate::repetitions::CardSetSettings;
2026-04-14 15:19:46 +03:00
use crate::Page::PreviousPage;
2026-04-16 09:56:57 +03:00
use crate::{AppState, KeyPressedPage, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
2026-04-14 15:19:46 +03:00
use iced::alignment::Horizontal::Center;
use iced::keyboard::key::Physical::Code;
2026-04-19 20:22:35 +03:00
use iced::widget::{button, column, container, row, rule, space, text};
2026-04-14 15:19:46 +03:00
use iced::{alignment, keyboard, Element, Fill, Left, Task};
2026-04-19 22:56:58 +03:00
use rodio::MixerDeviceSink;
2026-04-14 15:19:46 +03:00
use std::sync::{Arc, Mutex};
2026-04-19 22:56:58 +03:00
use tokio::task::spawn_blocking;
2026-04-09 22:45:31 +03:00
pub struct RepetitionState {
2026-04-14 15:19:46 +03:00
pub settings: CardSetSettings,
pub set: CardSet,
2026-04-13 22:25:58 +03:00
pub state: Arc<Mutex<AppState>>,
2026-04-14 15:19:46 +03:00
current_word: DictionaryElement,
open: bool,
2026-04-19 22:56:58 +03:00
can_play: bool,
sink: Arc<MixerDeviceSink>,
2026-04-09 22:45:31 +03:00
}
impl NavigatedPage<RepetitionMessage> for RepetitionState {
fn navigate(&self, message: &RepetitionMessage) -> Option<Page> {
if let RepetitionMessage::Back = message {
Some(PreviousPage)
} else {
None
}
}
}
impl RepetitionState {
2026-04-13 22:25:58 +03:00
pub(crate) fn new(set: CardSetSettings, state: Arc<Mutex<AppState>>) -> RepetitionState {
2026-04-14 15:19:46 +03:00
let mut card_set = CardSet::new(&set, state.clone());
let word = card_set.next();
2026-04-19 22:56:58 +03:00
let sink_handle = rodio::DeviceSinkBuilder::open_default_sink().unwrap();
2026-04-09 22:45:31 +03:00
RepetitionState {
2026-04-14 15:19:46 +03:00
settings: set,
set: card_set,
state,
current_word: word,
open: false,
2026-04-19 22:56:58 +03:00
can_play: true,
sink: Arc::new(sink_handle)
2026-04-09 22:45:31 +03:00
}
}
}
impl RepetitionState {
2026-04-14 15:19:46 +03:00
pub fn update(&mut self, message: RepetitionMessage) -> Task<RootMessage> {
match message {
RepetitionMessage::Back => {}
2026-04-20 10:13:39 +03:00
RepetitionMessage::Next => return self.next(),
RepetitionMessage::Answer(m) => return self.answer(m),
2026-04-19 22:56:58 +03:00
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;
}
2026-04-14 15:19:46 +03:00
}
2026-04-09 22:45:31 +03:00
Task::none()
}
2026-04-19 22:56:58 +03:00
2026-04-20 10:13:39 +03:00
fn next(&mut self) -> Task<RootMessage> {
2026-04-14 15:19:46 +03:00
if self.open {
self.set.open(WordOpenMode::None);
self.current_word = self.set.next();
2026-04-15 07:53:32 +03:00
self.open = false;
2026-04-20 10:13:39 +03:00
return Task::perform(play_sound(self.sink.clone(), self.current_word.key.clone()), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished));
2026-04-14 15:19:46 +03:00
} else {
self.open = true;
2026-04-20 10:13:39 +03:00
return Task::none();
2026-04-14 15:19:46 +03:00
}
}
2026-04-20 10:13:39 +03:00
fn answer(&mut self, mode: WordOpenMode) -> Task<RootMessage> {
2026-04-14 15:19:46 +03:00
if !self.open {
2026-04-20 10:13:39 +03:00
return Task::none();
2026-04-14 15:19:46 +03:00
}
self.set.open(mode);
self.open = false;
self.current_word = self.set.next();
2026-04-20 10:13:39 +03:00
return Task::perform(play_sound(self.sink.clone(), self.current_word.key.clone()), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished));
2026-04-14 15:19:46 +03:00
}
2026-04-09 22:45:31 +03:00
pub fn view(&self) -> Element<'_, RepetitionMessage> {
container(
iced::widget::column![
button("Назад").on_press(RepetitionMessage::Back),
2026-04-14 15:19:46 +03:00
column![
container(self.draw_forward())
.width(Fill)
.height(Fill)
.align_x(Center)
.align_y(alignment::Vertical::Center),
2026-04-19 20:22:35 +03:00
rule::horizontal(2),
2026-04-14 15:19:46 +03:00
container(self.draw_backward())
.width(Fill)
.height(Fill)
.align_x(Center)
.align_y(alignment::Vertical::Center),
2026-04-19 20:22:35 +03:00
container(self.answer_bar())
.width(Fill)
.align_x(Center)
.height(30)
2026-04-14 15:19:46 +03:00
]
.height(Fill)
.width(Fill)
]
.align_x(Left)
.width(Fill),
2026-04-09 22:45:31 +03:00
)
2026-04-14 15:19:46 +03:00
.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),
2026-04-20 10:13:39 +03:00
"speech" => self.draw_voice(),
2026-04-14 15:19:46 +03:00
_ => 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),
2026-04-20 10:13:39 +03:00
"speech" => self.draw_voice(),
2026-04-14 15:19:46 +03:00
_ => 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)),
]
2026-04-16 09:56:57 +03:00
.spacing(DEFAULT_SPACING)
2026-04-14 15:19:46 +03:00
.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()
2026-04-09 22:45:31 +03:00
}
2026-04-19 22:56:58 +03:00
2026-04-20 10:13:39 +03:00
fn draw_voice(&self) -> Element<'_, RepetitionMessage> {
2026-04-19 22:56:58 +03:00
button("Воспроизвести")
.on_press(RepetitionMessage::Play)
.into()
}
2026-04-09 22:45:31 +03:00
}
2026-04-14 15:19:46 +03:00
impl KeyPressedPage for RepetitionState {
2026-04-20 10:13:39 +03:00
fn press(&mut self, message: &keyboard::Event) -> iced::Task<RootMessage> {
2026-04-14 15:19:46 +03:00
if let keyboard::Event::KeyPressed {
key: _,
modified_key: _,
physical_key: pk,
location: _,
modifiers: _,
text: _,
repeat: _,
} = message
{
if let Code(code) = pk {
2026-04-20 10:13:39 +03:00
return match code {
2026-04-14 15:19:46 +03:00
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),
2026-04-20 10:13:39 +03:00
_ => Task::none(),
2026-04-14 15:19:46 +03:00
}
}
2026-04-20 10:13:39 +03:00
2026-04-14 15:19:46 +03:00
}
2026-04-20 10:13:39 +03:00
Task::none()
2026-04-14 15:19:46 +03:00
}
}
#[derive(Clone)]
2026-04-09 22:45:31 +03:00
pub enum RepetitionMessage {
Next,
Back,
2026-04-14 15:19:46 +03:00
Answer(WordOpenMode),
2026-04-19 22:56:58 +03:00
Play,
PlayFinished,
2026-04-09 22:45:31 +03:00
}
2026-04-19 22:56:58 +03:00
async fn play_sound(sink: Arc<MixerDeviceSink>, text: String) {
let data = get_voice(text.as_str()).await;
spawn_blocking(move || {
rodio::play(&sink.mixer(), data).unwrap().sleep_until_end();
}).await.unwrap();
}