From 87a9fae91a2674e2124b836fc8ad415a51e8530b Mon Sep 17 00:00:00 2001 From: Mikhail Mitrofanov Date: Tue, 10 Feb 2026 17:04:48 +0300 Subject: [PATCH] Writing test page --- src/lang.rs | 14 +++++-- src/main.rs | 10 +++-- src/selector.rs | 24 ++++++++--- src/writing.rs | 107 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 src/writing.rs diff --git a/src/lang.rs b/src/lang.rs index 0eb1354..610df6b 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -180,6 +180,14 @@ impl KanaSet { } pub fn next(&mut self) -> (String, String) { + let current_set = self.list(); + + let mut rand = rand::rng(); + let index: u32 = rand.random(); + current_set[index as usize % current_set.len()].clone() + } + + pub fn list(&self) -> Vec<(String, String)> { let mut current_set: Vec<(String, String)> = Vec::new(); for i in 0..10 { @@ -189,10 +197,8 @@ impl KanaSet { }); } } - - let mut rand = rand::rng(); - let index: u32 = rand.random(); - current_set[index as usize % current_set.len()].clone() + + current_set } } diff --git a/src/main.rs b/src/main.rs index 5b1ddfd..fb298e1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,14 @@ mod lang; mod quiz; mod selector; +mod writing; use crate::quiz::*; use crate::selector::*; -use crate::Page::{Quiz, Selector}; +use crate::Page::{Quiz, Selector, Writing}; use iced::widget::text; use iced::Element; +use crate::writing::{WritingMessage, WritingState}; fn main() -> iced::Result { iced::application("A kana learn app", ScreenState::update, ScreenState::view).run() @@ -16,11 +18,13 @@ fn main() -> iced::Result { pub enum RootMessage { Selector(SelectorMessage), Quiz(QuizMessage), + Writing(WritingMessage), } enum Page { Selector(SelectorState), Quiz(QuizState), + Writing(WritingState), Back, } @@ -44,11 +48,11 @@ impl Default for ScreenState { impl ScreenState { pub fn update(&mut self, message: RootMessage) { - state_update!(message, self.stack, Selector, Quiz); + state_update!(message, self.stack, Selector, Quiz, Writing); } pub fn view(&self) -> Element<'_, RootMessage> { - view_navigation!(self.stack, Quiz, Selector) + view_navigation!(self.stack, Quiz, Selector, Writing) } } diff --git a/src/selector.rs b/src/selector.rs index 93cf9ac..e7519c4 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1,17 +1,21 @@ use crate::lang::{KanaSet, KanaType}; -use crate::Page::Quiz; +use crate::selector::SelectorMessage::ChangeMode; +use crate::writing::WritingState; +use crate::Page::{Quiz, Writing}; use crate::{NavigatedPage, Page, QuizState}; -use iced::widget::{button, checkbox, container, text, Column, Row}; +use iced::widget::*; use iced::{alignment, Element, Font}; pub struct SelectorState { pub set: KanaSet, + is_writing: bool, } impl Default for SelectorState { fn default() -> Self { Self { set: KanaSet::hiragana(), + is_writing: false, } } } @@ -21,14 +25,20 @@ pub enum SelectorMessage { Change, Goto, Check(usize, bool), + ChangeMode(bool), } impl NavigatedPage for SelectorState { fn navigate(&self, message: &SelectorMessage) -> Option { if let SelectorMessage::Goto = message { - let mut quiz = QuizState::new(); - quiz.set = self.set.clone(); - return Some(Quiz(quiz)); + return if self.is_writing { + let writing = WritingState::new(&self.set); + Some(Writing(writing)) + } else { + let mut quiz = QuizState::new(); + quiz.set = self.set.clone(); + Some(Quiz(quiz)) + }; } None } @@ -43,6 +53,7 @@ impl SelectorState { }, SelectorMessage::Goto => {} SelectorMessage::Check(i, b) => self.set.include_map[i] = b, + ChangeMode(b) => self.is_writing = b, } } @@ -51,6 +62,9 @@ impl SelectorState { iced::widget::column![ button("Переключить").on_press(SelectorMessage::Change), self.rows_selector(), + toggler(self.is_writing) + .label("Режим письма") + .on_toggle(ChangeMode), button("К тесту").on_press(SelectorMessage::Goto), ] .spacing(10), diff --git a/src/writing.rs b/src/writing.rs new file mode 100644 index 0000000..e4583ef --- /dev/null +++ b/src/writing.rs @@ -0,0 +1,107 @@ +use crate::lang::KanaSet; +use crate::Page::Back; +use crate::{NavigatedPage, Page}; +use iced::widget::*; +use iced::{alignment, Element, Fill, Font}; +use rand::seq::SliceRandom; + +#[derive(Clone, Debug)] +pub struct WritingState { + kana: String, + set: Vec<(String, String)>, + original_set: Vec<(String, String)>, + kana_total: String, + roman_total: String, + is_help: bool, + next_text: String, +} + +impl NavigatedPage for WritingState { + fn navigate(&self, message: &WritingMessage) -> Option { + if let WritingMessage::Back = message { + Some(Back) + } else { + None + } + } +} + +impl WritingState { + pub(crate) fn new(set: &KanaSet) -> WritingState { + let mut list = set.list(); + list.shuffle(&mut rand::rng()); + WritingState { + kana: "".to_string(), + set: list.clone(), + original_set: list, + kana_total: "".to_string(), + roman_total: "".to_string(), + is_help: false, + next_text: "Дальше".to_string(), + } + } +} + +impl WritingState { + pub fn update(&mut self, message: WritingMessage) { + match message { + WritingMessage::Back => todo!(), + WritingMessage::Next => self.next(), + } + } + + fn next(&mut self) { + if self.set.is_empty() { + self.kana = "".to_string(); + self.roman_total = "".to_string(); + self.kana_total = "".to_string(); + + self.set = self.original_set.clone(); + return; + } + + let current = self.set.pop().unwrap(); + self.kana_total += &*format!("{} ", ¤t.0).to_string(); + self.roman_total += &*format!("{} ", ¤t.1.clone()).to_string(); + self.kana = current.1; + } + + pub fn view(&self) -> Element<'_, WritingMessage> { + container( + iced::widget::column![ + text!("{}", self.roman_total).size(24), + text!("{}", self.kana).size(48), + self.answers(), + row![ + button(text!("{}", self.next_text)).on_press(WritingMessage::Next), + button("Закончить").on_press(WritingMessage::Back), + ] + .spacing(10) + ] + .spacing(10) + .align_x(alignment::Horizontal::Center), + ) + .center_x(Fill) + .into() + } + + fn answers(&self) -> Element<'_, WritingMessage> { + if self.set.is_empty() { + text!("{}", self.kana_total).font(Font::with_name("AppleGothic")).size(36).into() + } else { + vertical_space().height(36).into() + } + } +} +#[derive(Debug, Clone)] +pub enum WritingMessage { + Next, + Back, +} + +#[derive(Default, Debug, Clone)] +pub struct Score { + total: i32, + correct: i32, + fail: i32, +}