Files
jap_learn/src/pages/dictionary_quiz.rs
T
2026-09-11 00:01:19 +03:00

234 lines
6.8 KiB
Rust

use crate::app::AppState;
use crate::data::entities::{EntityHandle, WordData};
use crate::data::repositories::Repository;
use crate::jl_button;
use crate::navigation::Page::PreviousPage;
use crate::navigation::RootMessage;
use crate::navigation::*;
use crate::pages::dictionary::split_with_coma;
use crate::pages::dictionary_quiz::DictionaryQuizMessage::*;
use crate::pages::quiz::Score;
use crate::styling::*;
use iced::Background::Color;
use iced::border::Radius;
use iced::widget::container::Style;
use iced::widget::{Row, container, row, space, text, text_input};
use iced::{Border, Element, Fill, Task, Theme, alignment};
use rand::prelude::SliceRandom;
pub struct DictionaryQuizState {
state: AppState,
words: Vec<EntityHandle<WordData>>,
current_set: Vec<EntityHandle<WordData>>,
answer: String,
view: String,
correct: String,
score: Score,
is_help: bool,
reverse: bool,
laps: u32,
no_typing: bool,
}
#[derive(Debug, Clone)]
pub enum DictionaryQuizMessage {
Back,
AnswerChanged(String),
SubmitAnswer,
Appeal,
}
impl NavigatedPage<DictionaryQuizMessage> for DictionaryQuizState {
fn navigate(&mut self, message: &DictionaryQuizMessage) -> Option<Page> {
match message {
Back => Some(PreviousPage),
_ => None,
}
}
fn navigated(&mut self) {}
fn update(&mut self, message: DictionaryQuizMessage) -> Task<RootMessage> {
match message {
Back => {}
AnswerChanged(c) => self.answer = c.clone(),
SubmitAnswer => self.submit(),
Appeal => self.appeal_answer(),
}
Task::none()
}
fn view(&self) -> Element<'_, DictionaryQuizMessage> {
container(
iced::widget::column![
self.laps(),
iced::widget::column![
text!("{}", self.view).size(54),
text!(
"{}",
if self.is_help {
self.correct.clone()
} else {
String::new()
}
)
.size(ACCENT_FONT_SIZE)
.align_y(alignment::Vertical::Center),
]
.align_x(alignment::Horizontal::Center)
.spacing(5),
text_input("Перевод", &self.answer)
.size(28)
.width(250)
.on_input(AnswerChanged)
.on_submit(SubmitAnswer),
row![
text!("{}", self.score.total.to_string()).size(25),
text!("{}", self.score.correct.to_string())
.size(25)
.color(iced::Color::from_rgb8(60, 170, 60)),
text!("{}", self.score.fail.to_string())
.color(iced::Color::from_rgb8(255, 79, 0))
.size(25),
]
.spacing(DEFAULT_SPACING),
row![jl_button!("Закончить").on_press(Back), self.appeal_button()]
.spacing(DEFAULT_SPACING),
]
.spacing(DEFAULT_SPACING)
.align_x(alignment::Horizontal::Center),
)
.center_y(Fill)
.center_x(Fill)
.into()
}
}
impl DictionaryQuizState {
pub fn new(
words: Vec<EntityHandle<WordData>>,
reverse: bool,
no_typing: bool,
state: AppState,
) -> DictionaryQuizState {
Self {
state,
words,
current_set: Vec::new(),
answer: "".to_string(),
view: "---".to_string(),
correct: "".to_string(),
score: Default::default(),
is_help: false,
reverse,
laps: 0,
no_typing,
}
}
fn submit(&mut self) {
if self.view == "---" {
self.show_next();
return;
}
if self.no_typing {
self.no_type_submit();
} else {
self.default_submit();
}
}
fn no_type_submit(&mut self) {
if self.is_help {
self.is_help = false;
self.score.total += 1;
self.show_next()
} else {
self.is_help = true;
}
}
fn default_submit(&mut self) {
if !self.is_help {
self.score.total += 1;
}
if self.answer.to_lowercase() == self.correct.to_lowercase()
|| split_with_coma(self.correct.to_lowercase().as_str())
.contains(&self.answer.to_lowercase())
{
if !self.is_help {
self.score.correct += 1;
}
self.show_next()
} else {
self.score.fail += 1;
self.is_help = true;
}
}
fn update_set(&mut self) {
self.current_set.append(&mut self.words.clone());
self.current_set.shuffle(&mut rand::rng());
if self.score.total != 0 {
self.laps += 1;
}
}
fn show_next(&mut self) {
self.is_help = false;
self.answer = String::new();
let next: EntityHandle<WordData> = match self.current_set.pop() {
Some(word) => word,
None => {
self.update_set();
self.current_set.pop().expect("Empty set")
}
};
let repo = self.state.dictionary.read();
let next = repo.get_static(next).unwrap();
if self.reverse {
self.view = next.value;
self.correct = next.key;
} else {
self.view = next.key;
self.correct = next.value;
}
}
fn laps(&self) -> Element<'_, DictionaryQuizMessage> {
let mut col = Row::new();
for _ in 0..self.laps {
col = col.push(iced::widget::container(space().height(15).width(15)).style(
|x: &Theme| Style {
text_color: None,
background: Some(Color(x.palette().primary)),
border: Border {
color: Default::default(),
width: 0.0,
radius: Radius::new(5),
},
shadow: Default::default(),
snap: false,
},
));
}
col.spacing(DEFAULT_SPACING).into()
}
fn appeal_button(&self) -> Element<'_, DictionaryQuizMessage> {
if self.is_help && !self.no_typing {
return jl_button!("Апелляция").on_press(Appeal).into();
}
space().into()
}
fn appeal_answer(&mut self) {
self.score.correct += 1;
self.score.fail -= 1;
self.answer = self.correct.clone();
self.show_next()
}
}