diff --git a/Cargo.lock b/Cargo.lock index f1034ec..48b8829 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,6 +25,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "const-random", "getrandom 0.3.4", "once_cell", "version_check", @@ -509,6 +510,26 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1419,6 +1440,7 @@ dependencies = [ "dirs", "iced", "rand", + "rhai", "serde", "serde_json", ] @@ -2120,6 +2142,9 @@ name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +dependencies = [ + "portable-atomic", +] [[package]] name = "option-ext" @@ -2438,6 +2463,34 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" +[[package]] +name = "rhai" +version = "1.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f9ef5dabe4c0b43d8f1187dc6beb67b53fe607fff7e30c5eb7f71b814b8c2c1" +dependencies = [ + "ahash", + "bitflags 2.10.0", + "num-traits", + "once_cell", + "rhai_codegen", + "smallvec", + "smartstring", + "thin-vec", + "web-time", +] + +[[package]] +name = "rhai_codegen" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4322a2a4e8cf30771dd9f27f7f37ca9ac8fe812dddd811096a98483080dabe6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "roxmltree" version = "0.20.0" @@ -2635,6 +2688,17 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +[[package]] +name = "smartstring" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb72c633efbaa2dd666986505016c32c3044395ceaf881518399d2f4127ee29" +dependencies = [ + "autocfg", + "static_assertions", + "version_check", +] + [[package]] name = "smithay-client-toolkit" version = "0.19.2" @@ -2818,6 +2882,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thin-vec" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da322882471314edc77fa5232c587bcb87c9df52bfd0d7d4826f8868ead61899" + [[package]] name = "thiserror" version = "1.0.69" @@ -2858,6 +2928,15 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tiny-skia" version = "0.11.4" diff --git a/Cargo.toml b/Cargo.toml index 7c63db9..86455d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,4 +8,5 @@ iced = { version = "0.14.0"} rand = "0.10.0" dirs = "6.0.0" serde = { version = "1.0.144", features = ["derive"] } -serde_json = "1.0.149" \ No newline at end of file +serde_json = "1.0.149" +rhai = "1.24.0" diff --git a/src/dictionary.rs b/src/dictionary.rs index 3770979..9835dc9 100644 --- a/src/dictionary.rs +++ b/src/dictionary.rs @@ -1,6 +1,6 @@ use crate::dictionary::DictionaryMessage::Test; use crate::dictionary_test::DictionaryQuizState; -use crate::{NavigatedPage, Page, RootMessage}; +use crate::{AppState, NavigatedPage, Page, RootMessage}; use iced::alignment::Vertical::Center; use iced::widget::button::Style; use iced::widget::*; @@ -8,14 +8,13 @@ use iced::{Border, Color, Length, Shadow, Task}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::fs; -use std::fs::File; -use std::io::Read; use std::path::PathBuf; +use std::sync::{Arc, Mutex}; use DictionaryMessage::Back; -#[derive(Clone, Debug)] +#[derive(Clone)] pub struct DictionaryState { - dict: Vec, + state: Arc>, include_map: Vec, tag_map: HashMap, reverse: bool, @@ -28,6 +27,7 @@ pub struct DictionaryElement { pub key: String, pub value: String, pub tags: String, + pub additional: HashMap, } impl DictionaryElement { @@ -36,6 +36,7 @@ impl DictionaryElement { key: String::new(), value: String::new(), tags: String::new(), + additional: Default::default(), } } } @@ -66,9 +67,11 @@ impl NavigatedPage for DictionaryState { if let Test = message { if self.include_map.iter().any(|x| *x) { let mut words = vec![]; + let dict = &self.state.lock().unwrap().dictionary; + for i in 0..self.include_map.len() { if self.include_map[i] { - words.push(self.dict[i].clone()); + words.push(dict[i].clone()); } } return Some(Page::DictionaryQuiz(DictionaryQuizState::new( @@ -82,25 +85,12 @@ impl NavigatedPage for DictionaryState { } } -impl Default for DictionaryState { - fn default() -> Self { - Self::new() - } -} impl DictionaryState { - pub fn new() -> Self { - let mut current_dict = "[]".to_string(); - match File::open(dict_file()) { - Ok(mut f) => { - current_dict = String::new(); - f.read_to_string(&mut current_dict).unwrap(); - } - _ => {} - } - let list: Vec = serde_json::from_str(¤t_dict).unwrap(); + pub fn new(state: Arc>) -> Self { + let len = state.lock().unwrap().dictionary.len(); let mut result = DictionaryState { - include_map: vec![false; list.len()], - dict: list, + include_map: vec![false; len], + state, tag_map: HashMap::new(), reverse: false, search: "".to_string(), @@ -115,17 +105,29 @@ impl DictionaryState { pub fn update(&mut self, message: DictionaryMessage) -> Task { match message { DictionaryMessage::NewWord => { - self.dict.push(DictionaryElement::new()); + let dict = &mut self.state.lock().unwrap().dictionary; + dict.push(DictionaryElement::new()); self.include_map.push(false); } - DictionaryMessage::SetKey(i, v) => self.dict[i].key = v, - DictionaryMessage::SetValue(i, v) => self.dict[i].value = v, + DictionaryMessage::SetKey(i, v) => { + let dict = &mut self.state.lock().unwrap().dictionary; + dict[i].key = v + } + DictionaryMessage::SetValue(i, v) => { + let dict = &mut self.state.lock().unwrap().dictionary; + dict[i].value = v + }, DictionaryMessage::SetTags(i, v) => { - self.dict[i].tags = v; + { + let dict = &mut self.state.lock().unwrap().dictionary; + dict.get_mut(i).unwrap().tags = v; + } + self.update_tags(); } DictionaryMessage::Remove(i) => { - self.dict.remove(i); + let dict = &mut self.state.lock().unwrap().dictionary; + dict.remove(i); } DictionaryMessage::Include(i, b) => self.include_map[i] = b, DictionaryMessage::IncludeTag(t, v) => { @@ -138,7 +140,9 @@ impl DictionaryState { } DictionaryMessage::Save => { let dir = dict_file(); - let content = serde_json::to_string_pretty(&self.dict.clone()).unwrap(); + let dict = &self.state.lock().unwrap().dictionary; + + let content = serde_json::to_string_pretty(&dict.clone()).unwrap(); fs::write(dir, content.clone()) .unwrap_or_else(|e| println!("Can't write file: {}", e)); } @@ -178,7 +182,9 @@ impl DictionaryState { let mut col = Column::new().width(Length::Fill); let mut i = 0; - for word in &self.dict { + let dict = &self.state.lock().unwrap().dictionary; + + for word in dict { if !self.search.is_empty() { if word.key.contains(&self.search) == false && word.value.contains(&self.search) == false @@ -237,11 +243,13 @@ impl DictionaryState { } fn filters(&self) -> iced::Element<'_, DictionaryMessage> { + let dict = &self.state.lock().unwrap().dictionary; + iced::widget::column![ text_input("Поиск", &self.search) .on_input(DictionaryMessage::Search) .width(Length::Fill), - text!("Всего слов: {}", self.dict.len()), + text!("Всего слов: {}", dict.len()), text!( "Выбрано слов: {}", self.include_map.iter().filter(|i| **i).count() @@ -290,7 +298,9 @@ impl DictionaryState { fn update_tags(&mut self) { let mut tags_list: Vec = vec![]; - for element in &self.dict { + let dict = &self.state.lock().unwrap().dictionary; + + for element in dict { tags_list.append(&mut split_with_coma(element.tags.clone())); } @@ -325,8 +335,10 @@ impl DictionaryState { return; } + let dict = &self.state.lock().unwrap().dictionary; + for i in 0..self.include_map.len() { - let tags = split_with_coma(self.dict[i].tags.clone()); + let tags = split_with_coma(dict[i].tags.clone()); if tags.iter().all(|t| include_tags.contains(t)) { self.include_map[i] = true; } else { @@ -343,7 +355,7 @@ pub fn split_with_coma(ts: String) -> Vec { .collect::>() } -fn dict_file() -> PathBuf { +pub fn dict_file() -> PathBuf { let mut dir = dirs::data_dir().unwrap(); dir.push("jap_learn"); if !dir.exists() { diff --git a/src/main.rs b/src/main.rs index a080f42..24f1c49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,26 +1,38 @@ #![windows_subsystem = "windows"] -mod lang; -mod quiz; -mod selector; -mod writing; mod dictionary; mod dictionary_test; +mod lang; +mod quiz; mod randomizer; +mod repetition; +mod repetitions; +mod selector; +mod writing; +use std::fs::File; +use std::io::Read; +use crate::dictionary::{DictionaryElement, DictionaryMessage, DictionaryState}; +use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState}; use crate::quiz::*; +use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState}; +use crate::repetition::{RepetitionMessage, RepetitionState}; +use crate::repetitions::{RepetitionsMessage, RepetitionsState}; use crate::selector::*; use crate::writing::{WritingMessage, WritingState}; -use crate::Page::{Dictionary, DictionaryQuiz, Quiz, Randomizer, Selector, Writing}; +use crate::Page::{ + Dictionary, DictionaryQuiz, Quiz, Randomizer, Repetition, Repetitions, Selector, Writing, +}; use iced::widget::text; -use iced::{Font, Task}; use iced::Element; -use crate::dictionary::{DictionaryMessage, DictionaryState}; -use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState}; -use crate::randomizer::randomizer::{ RandomizerMessage, RandomizerState}; +use iced::{Font, Task}; +use std::sync::{Arc, Mutex}; fn main() -> iced::Result { iced::application(ScreenState::boot, ScreenState::update, ScreenState::view) - .title("Kana learn app").font(include_bytes!("../noto.ttf")).default_font(Font::with_name("Noto Sans JP")).run() + .title("Kana learn app") + .font(include_bytes!("../noto.ttf")) + .default_font(Font::with_name("Noto Sans JP")) + .run() } #[derive(Debug, Clone)] @@ -30,7 +42,9 @@ pub enum RootMessage { Writing(WritingMessage), Dictionary(DictionaryMessage), DictionaryQuiz(DictionaryQuizMessage), - Randomizer(RandomizerMessage) + Randomizer(RandomizerMessage), + Repetitions(RepetitionsMessage), + Repetition(RepetitionMessage), } enum Page { @@ -40,38 +54,70 @@ enum Page { Dictionary(DictionaryState), DictionaryQuiz(DictionaryQuizState), Randomizer(RandomizerState), + Repetitions(RepetitionsState), + Repetition(RepetitionState), PreviousPage, } -impl Default for Page { - fn default() -> Self { - Selector(SelectorState::default()) - } -} - pub struct ScreenState { stack: Vec, } +pub struct AppState { + pub dictionary: Vec, +} + impl Default for ScreenState { fn default() -> Self { + let mut current_dict = "[]".to_string(); + match File::open(dictionary::dict_file()) { + Ok(mut f) => { + current_dict = String::new(); + f.read_to_string(&mut current_dict).unwrap(); + } + _ => {} + } + + let list: Vec = serde_json::from_str(¤t_dict).unwrap(); + let state = Arc::new(Mutex::new(AppState { dictionary: list })); ScreenState { - stack: vec![Page::default()], + stack: vec![Selector(SelectorState::new(state.clone()))], } } } impl ScreenState { - pub fn boot() -> (ScreenState, Task){ + pub fn boot() -> (ScreenState, Task) { (ScreenState::default(), Task::none()) } - pub fn update(&mut self, message: RootMessage) -> Task { - state_update!(message, self.stack, Selector, Quiz, Writing, Dictionary, DictionaryQuiz, Randomizer); + pub fn update(&mut self, message: RootMessage) -> Task { + state_update!( + message, + self.stack, + Selector, + Quiz, + Writing, + Dictionary, + DictionaryQuiz, + Randomizer, + Repetitions, + Repetition + ); Task::none() } pub fn view(&self) -> Element<'_, RootMessage> { - view_navigation!(self.stack, Quiz, Selector, Writing, Dictionary, DictionaryQuiz, Randomizer) + view_navigation!( + self.stack, + Quiz, + Selector, + Writing, + Dictionary, + DictionaryQuiz, + Randomizer, + Repetitions, + Repetition + ) } } @@ -119,4 +165,4 @@ macro_rules! message_navigation { trait NavigatedPage { fn navigate(&self, message: &T) -> Option; -} \ No newline at end of file +} diff --git a/src/repetition.rs b/src/repetition.rs new file mode 100644 index 0000000..39dac2c --- /dev/null +++ b/src/repetition.rs @@ -0,0 +1,57 @@ +use crate::Page::PreviousPage; +use crate::{NavigatedPage, Page, RootMessage}; +use iced::widget::{button, container}; +use iced::{Element, Fill, Left, Task}; + +#[derive(Clone, Debug)] +pub struct RepetitionState { +} + +impl Default for RepetitionState { + fn default() -> Self { + RepetitionState::new() + } +} + +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() -> RepetitionState { + RepetitionState { + + } + } +} + +impl RepetitionState { + pub fn update(&mut self, message: RepetitionMessage) -> Task { + Task::none() + } + + + pub fn view(&self) -> Element<'_, RepetitionMessage> { + container( + iced::widget::column![ + button("Назад").on_press(RepetitionMessage::Back), + + ].align_x(Left).width(Fill) + ) + .center_x(Fill) + .padding(10) + .into() + } +} +#[derive(Debug, Clone)] +pub enum RepetitionMessage { + Next, + Back, + SwitchShowMode(bool), +} diff --git a/src/repetitions.rs b/src/repetitions.rs new file mode 100644 index 0000000..7f0fcb0 --- /dev/null +++ b/src/repetitions.rs @@ -0,0 +1,276 @@ +use crate::dictionary::DictionaryElement; +use crate::Page::PreviousPage; +use crate::{AppState, NavigatedPage, Page, RootMessage}; +use iced::widget::button::{Catalog, Style}; +use iced::widget::{button, column, container, row, scrollable, space, text, text_input, Column}; +use iced::Background::Color; +use iced::{Border, Center, Element, Fill, Left, Length, Shadow, Task, Theme}; +use rhai::{Engine, Scope}; +use std::sync::{Arc, Mutex}; + +#[derive(Clone)] +pub struct RepetitionsState { + sets: Vec, + selected_set: Option, + correct_filters: Vec, + pub state: Arc>, +} + +impl NavigatedPage for RepetitionsState { + fn navigate(&self, message: &RepetitionsMessage) -> Option { + if let RepetitionsMessage::Back = message { + Some(PreviousPage) + } else { + None + } + } +} + +impl RepetitionsState { + pub(crate) fn new(state: Arc>) -> RepetitionsState { + RepetitionsState { + sets: Vec::new(), + selected_set: None, + correct_filters: vec![], + state, + } + } +} + +impl RepetitionsState { + pub fn update(&mut self, message: RepetitionsMessage) -> Task { + match message { + RepetitionsMessage::Next => {} + RepetitionsMessage::Back => {} + RepetitionsMessage::GoToRepetition => {} + RepetitionsMessage::CreateSet => { + self.sets.push(CardSet::with_name(format!( + "Card set #{}", + self.sets.len() + 1 + ))); + self.correct_filters.push(true); + } + RepetitionsMessage::DeleteSet => { + self.sets.remove(self.selected_set.unwrap()); + self.correct_filters.remove(self.selected_set.unwrap()); + self.selected_set = None; + } + RepetitionsMessage::SelectSet(index) => { + self.selected_set = Some(index); + } + RepetitionsMessage::SetName(new) => { + self.sets[self.selected_set.unwrap()].name = new; + } + RepetitionsMessage::Save => { + for i in 0..self.sets.len() { + self.correct_filters[i] = self.sets[i].check_filter(); + } + + if self.correct_filters.iter().all(|x| *x) { + println!("Saving"); + } else { + println!("Some errors"); + } + } + RepetitionsMessage::SetForward(new) => { + self.sets[self.selected_set.unwrap()].forward = new; + } + RepetitionsMessage::SetBackward(new) => { + self.sets[self.selected_set.unwrap()].backward = new; + } + RepetitionsMessage::SetFilter(new) => { + self.sets[self.selected_set.unwrap()].filter = new; + } + RepetitionsMessage::TryFilter => { + let set = &mut self.sets[self.selected_set.unwrap()]; + let count = set.get_word_list(&self.state.lock().unwrap()).len(); + set.count = Some(count); + } + } + Task::none() + } + + pub fn view(&self) -> Element<'_, RepetitionsMessage> { + container( + iced::widget::column![ + button("Назад").on_press(RepetitionsMessage::Back), + row![ + column![ + scrollable(self.sets_list()).height(Fill), + button("Добавить") + .width(Fill) + .on_press(RepetitionsMessage::CreateSet), + button("Сохранить") + .width(Fill) + .on_press(RepetitionsMessage::Save), + ] + .spacing(10) + .width(Length::FillPortion(1)), + self.selected_set_view(), + self.launch_button() + ] + .align_y(Center) + .padding(10) + .spacing(10) + .width(Fill) + .height(Fill) + ] + .align_x(Left) + .width(Fill), + ) + .center_x(Fill) + .padding(10) + .into() + } + + fn launch_button(&self) -> Element<'_, RepetitionsMessage> { + if let Some(_) = self.selected_set { + return button(text!("▷").height(Fill).center()) + .height(200) + .on_press(RepetitionsMessage::GoToRepetition) + .into(); + } + space().into() + } + + fn selected_set_view(&self) -> Element<'_, RepetitionsMessage> { + if let Some(index) = self.selected_set { + return column![ + scrollable( + column![ + text_input("Название набора", &self.sets[index].name) + .on_input(RepetitionsMessage::SetName), + text_input("Передняя сторона", &self.sets[index].forward) + .on_input(RepetitionsMessage::SetForward), + text_input("Задняя сторона", &self.sets[index].backward) + .on_input(RepetitionsMessage::SetBackward), + text!("Фильтр"), + text_input("", &self.sets[index].filter) + .on_input(RepetitionsMessage::SetFilter), + button("Проверить фильтр").on_press(RepetitionsMessage::TryFilter), + self.count_view() + ] + .spacing(10) + ) + .height(Fill), + button("Удалить") + .style(|x: &Theme, _status| Style { + background: Some(Color(x.palette().danger)), + text_color: x.palette().text, + border: Default::default(), + shadow: Default::default(), + snap: false, + }) + .on_press(RepetitionsMessage::DeleteSet), + ] + .spacing(10) + .width(Length::FillPortion(2)) + .into(); + } + space().width(Length::FillPortion(2)).into() + } + + fn count_view(&self) -> Element<'_, RepetitionsMessage> { + if let Some(count) = self.sets[self.selected_set.unwrap()].count { + return text!("Колличество слов: {}", count).into(); + } + space().into() + } + + fn sets_list(&self) -> Column<'_, RepetitionsMessage> { + let mut column = Column::new(); + let mut i = 0; + for set in &self.sets { + column = column.push( + button(text!("{}", set.name.clone())) + .on_press_with(move || RepetitionsMessage::SelectSet(i.clone())) + .style(move |_x: &Theme, _status| Style { + background: None, + text_color: if self.correct_filters[i.clone()] { + _x.palette().text + } else { + _x.palette().warning + }, + border: Border::default(), + shadow: Shadow::default(), + snap: false, + }), + ); + i += 1; + } + + column + } +} +#[derive(Debug, Clone)] +pub enum RepetitionsMessage { + Next, + Back, + GoToRepetition, + CreateSet, + DeleteSet, + SetName(String), + SelectSet(usize), + Save, + SetForward(String), + SetBackward(String), + SetFilter(String), + TryFilter, +} + +#[derive(Debug, Clone)] +pub struct CardSet { + name: String, + forward: String, + backward: String, + filter: String, + count: Option, +} + +impl CardSet { + fn with_name(name: String) -> CardSet { + CardSet { + name, + forward: "".to_string(), + backward: "".to_string(), + filter: "true".to_string(), + count: None, + } + } + + fn check_filter(&self) -> bool { + let engine = Engine::new(); + let ast = engine.compile(&self.filter); + ast.is_ok() + } + + pub fn get_word_list(&self, state: &AppState) -> Vec { + let mut list = vec![]; + let engine = Engine::new(); + let ast = engine.compile(&self.filter); + if ast.is_err() { + return list; + } + + let ast = ast.unwrap(); + + for word in &state.dictionary { + let mut more = rhai::Map::new(); + for iced in &word.additional { + more.insert(iced.0.clone().into(), iced.1.clone().into()); + } + let mut scope = Scope::new(); + scope.push_constant("key", word.key.clone()) + .push_constant("value", word.value.clone()) + .push_constant("tags", word.tags.clone()) + .push_constant("more", more); + + let result = engine.eval_ast_with_scope::(&mut scope, &ast); + if result.is_ok() && result.unwrap() { + list.push(word.clone()); + } + } + + list + } +} diff --git a/src/selector.rs b/src/selector.rs index 866f2aa..0ae0650 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -1,25 +1,19 @@ +use std::sync::{Arc, Mutex}; +use crate::dictionary::DictionaryState; use crate::lang::{KanaSet, KanaType}; +use crate::randomizer::randomizer::RandomizerState; +use crate::repetitions::RepetitionsState; use crate::selector::SelectorMessage::ChangeMode; use crate::writing::WritingState; use crate::Page::{Quiz, Writing}; -use crate::{NavigatedPage, Page, QuizState, RootMessage}; +use crate::{AppState, NavigatedPage, Page, QuizState, RootMessage}; use iced::widget::*; use iced::{alignment, Element, Task}; -use crate::dictionary::DictionaryState; -use crate::randomizer::randomizer::RandomizerState; pub struct SelectorState { pub set: KanaSet, is_writing: bool, -} - -impl Default for SelectorState { - fn default() -> Self { - Self { - set: KanaSet::hiragana(), - is_writing: false, - } - } + state: Arc> } #[derive(Debug, Clone)] @@ -30,6 +24,7 @@ pub enum SelectorMessage { ChangeMode(bool), ToDictionary, ToRandomize, + ToRepetitions, } impl NavigatedPage for SelectorState { @@ -45,17 +40,27 @@ impl NavigatedPage for SelectorState { }; } if let SelectorMessage::ToDictionary = message { - return Some(Page::Dictionary(DictionaryState::default())) + return Some(Page::Dictionary(DictionaryState::new(self.state.clone()))); } if let SelectorMessage::ToRandomize = message { - return Some(Page::Randomizer(RandomizerState::default())) + return Some(Page::Randomizer(RandomizerState::default())); + } + if let SelectorMessage::ToRepetitions = message { + return Some(Page::Repetitions(RepetitionsState::new(self.state.clone()))); } None } } impl SelectorState { - pub fn update(&mut self, message: SelectorMessage) -> Task { + pub fn new(state: Arc>) -> Self { + Self{ + set: Default::default(), + is_writing: false, + state, + } + } + pub fn update(&mut self, message: SelectorMessage) -> Task { match message { SelectorMessage::Change => match self.set.chars_type { KanaType::Katakana => self.set = KanaSet::hiragana(), @@ -71,9 +76,13 @@ impl SelectorState { pub fn view(&self) -> Element<'_, SelectorMessage> { container( iced::widget::column![ - row![button("Переключить азбуки").on_press(SelectorMessage::Change), - button("Словарь").on_press(SelectorMessage::ToDictionary), - button("Рандомайзер").on_press(SelectorMessage::ToRandomize)].spacing(10), + row![ + button("Переключить азбуки").on_press(SelectorMessage::Change), + button("Словарь").on_press(SelectorMessage::ToDictionary), + button("Рандомайзер").on_press(SelectorMessage::ToRandomize), + button("Повторение").on_press(SelectorMessage::ToRepetitions) + ] + .spacing(10), self.rows_selector(), toggler(self.is_writing) .label("Режим письма") @@ -82,7 +91,7 @@ impl SelectorState { ] .spacing(10), ) - .padding(20) + .padding(10) .into() }