diff --git a/src/dictionary.rs b/src/dictionary.rs index d69348f..4781f13 100644 --- a/src/dictionary.rs +++ b/src/dictionary.rs @@ -1,7 +1,5 @@ use crate::data_provider::words::{delete_group, delete_word, update_group, update_word}; -use crate::dictionary::DictionaryMessage::{ - ChangeDirection, DeleteGroup, EditGroup, SaveGroup, Test, -}; +use crate::dictionary::DictionaryMessage::*; use crate::dictionary_test::DictionaryQuizState; use crate::lang::{WordData, WordGroup}; use crate::navigation::Page::Word; @@ -9,7 +7,6 @@ use crate::navigation::{NavigatedPage, Page}; use crate::styling::*; use crate::word::WordState; use crate::{AppState, RootMessage}; -use DictionaryMessage::Back; use chrono::{DateTime, TimeDelta, Utc}; use iced::alignment::Vertical::Center; use iced::widget::button::Style; @@ -24,6 +21,8 @@ use std::ops::Add; use std::path::PathBuf; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; +use iced::widget::text_input::default; +use DictionaryMessage::Back; #[derive(Clone)] pub struct DictionaryState { @@ -88,7 +87,7 @@ impl NavigatedPage for DictionaryState { ))); } } - if let DictionaryMessage::WordAction(index) = message { + if let WordAction(index) = message { let word: WordData; { let state = self.state.lock().unwrap(); @@ -126,7 +125,7 @@ impl DictionaryState { pub fn update(&mut self, message: DictionaryMessage) -> Task { match message { - DictionaryMessage::NewWord => { + NewWord => { let mut state = self.state.lock().unwrap(); let mut word = WordData::new(); word.group_id = state.word_groups[self.selected_group_index].id.clone(); @@ -136,21 +135,21 @@ impl DictionaryState { self.include_map.push(false); } - DictionaryMessage::SetKey(i, v) => { + SetKey(i, v) => { { let dict = &mut self.state.lock().unwrap().dictionary; dict[i].key = v; } return self.launch_auto_save_offset(i); } - DictionaryMessage::SetValue(i, v) => { + SetValue(i, v) => { { let dict = &mut self.state.lock().unwrap().dictionary; dict[i].value = v } return self.launch_auto_save_offset(i); } - DictionaryMessage::SetTags(i, mut v) => { + SetTags(i, mut v) => { { let dict = &mut self.state.lock().unwrap().dictionary; @@ -172,7 +171,7 @@ impl DictionaryState { return self.launch_auto_save_offset(i); } - DictionaryMessage::WordAction(i) => { + WordAction(i) => { let state = &mut self.state.lock().unwrap(); let dict = &mut state.dictionary; let word = dict.remove(i); @@ -180,8 +179,8 @@ impl DictionaryState { self.auto_save_queue.remove(&i); delete_word(&word, &state.connection) } - DictionaryMessage::Include(i, b) => self.include_map[i] = b, - DictionaryMessage::IncludeTag(t, v) => { + Include(i, b) => self.include_map[i] = b, + IncludeTag(t, v) => { let index: u32; { let state = self.state.lock().unwrap(); @@ -191,19 +190,19 @@ impl DictionaryState { self.update_words_include(index) } - DictionaryMessage::ResetTags => { + ResetTags => { self.tag_map.iter_mut().for_each(|(_, v)| *v = false); self.include_map.iter_mut().for_each(|x| *x = false) } - DictionaryMessage::SetReverse(v) => self.reverse = v, - DictionaryMessage::Search(s) => { + SetReverse(v) => self.reverse = v, + Search(s) => { self.search = s; } - DictionaryMessage::SetTyping(b) => self.no_typing = b, - DictionaryMessage::SubmitWord(i) => self.save_word(i), + SetTyping(b) => self.no_typing = b, + SubmitWord(i) => self.save_word(i), Back => {} Test => {} - DictionaryMessage::CreateGroup => { + CreateGroup => { let state = &mut self.state.lock().unwrap(); state.word_groups.push(WordGroup { @@ -230,7 +229,7 @@ impl DictionaryState { update_group(group, connection); state.word_groups[self.selected_group_index] = group.clone(); } - DictionaryMessage::SelectGroup(i) => { + SelectGroup(i) => { self.selected_group_index = i; let index: u32; { @@ -256,7 +255,7 @@ impl DictionaryState { ChangeDirection => { self.reverse_list = !self.reverse_list; } - DictionaryMessage::TrySave(word_index) => { + TrySave(word_index) => { let now = Utc::now(); if !self.auto_save_queue.contains_key(&word_index) { return Task::none(); @@ -283,7 +282,7 @@ impl DictionaryState { fn launch_auto_save_offset(&mut self, index: usize) -> Task { let save_time = Utc::now().add(TimeDelta::milliseconds(900)); self.auto_save_queue.insert(index, save_time); - let message = RootMessage::Dictionary(DictionaryMessage::TrySave(index)); + let message = RootMessage::Dictionary(TrySave(index)); Task::perform( async { tokio::time::sleep(Duration::from_secs(1)).await }, |_| message, @@ -292,13 +291,18 @@ impl DictionaryState { pub fn view(&self) -> iced::Element<'_, DictionaryMessage> { back_overlay( - iced::widget::column![ - self.groups_panel(), - self.words_list(), - button("Добавить слово") - .style(jl_button) - .on_press(DictionaryMessage::NewWord), + row![ + iced::widget::column![ + self.groups_panel(), + self.words_list(), + button("Добавить слово") + .style(jl_button) + .on_press(NewWord), + ].spacing(5), + self.filters(), + ] + .spacing(5) .into(), Back, ) @@ -338,7 +342,7 @@ impl DictionaryState { line = line .push( checkbox(self.include_map[i]) - .on_toggle(move |b| DictionaryMessage::Include(i, b)), + .on_toggle(move |b| Include(i, b)), ) .push(space().width(10)); @@ -346,26 +350,41 @@ impl DictionaryState { text_input("Слово", &word.key) .size(ACCENT_FONT_SIZE) .width(Length::Fill) - .on_input(move |string| DictionaryMessage::SetKey(i, string)) - .on_submit(DictionaryMessage::SubmitWord(i)), + .on_input(move |string| SetKey(i, string)) + .on_submit(SubmitWord(i)) + .style(|x, status| { + let mut default_style = default(x, status); + default_style.border.radius = 0.0.into(); + default_style + }), ); line = line.push( text_input("Перевод", &word.value) .size(ACCENT_FONT_SIZE) .width(Length::Fill) - .on_input(move |string| DictionaryMessage::SetValue(i, string)) - .on_submit(DictionaryMessage::SubmitWord(i)), + .on_input(move |string| SetValue(i, string)) + .on_submit(SubmitWord(i)) + .style(|x, status| { + let mut default_style = default(x, status); + default_style.border.radius = 0.0.into(); + default_style + }), ); line = line.push( - text_input("Тэги", &word.tags) + text_input("Теги", &word.tags) .size(ACCENT_FONT_SIZE) .width(Length::Fill) - .on_input(move |string| DictionaryMessage::SetTags(i, string)) - .on_submit(DictionaryMessage::SubmitWord(i)), + .on_input(move |string| SetTags(i, string)) + .on_submit(SubmitWord(i)) + .style(|x, status| { + let mut default_style = default(x, status); + default_style.border.radius = 0.0.into(); + default_style + }), ); let line_button = || { - let action = DictionaryMessage::WordAction(i); + let action = WordAction(i); if word.id == 0 { return button("-").on_press(action).style(|_x, _status| Style { @@ -378,7 +397,7 @@ impl DictionaryState { } button("") - .on_press(DictionaryMessage::WordAction(i)) + .on_press(WordAction(i)) .width(15) }; @@ -396,7 +415,7 @@ impl DictionaryState { iced::widget::column![ text_input("Поиск", &self.search) - .on_input(DictionaryMessage::Search) + .on_input(Search) .width(Length::Fill), text!("Всего слов: {}", dict.len()), text!( @@ -406,10 +425,10 @@ impl DictionaryState { self.tags_selector(), toggler(self.no_typing) .label("Без набора") - .on_toggle(DictionaryMessage::SetTyping), + .on_toggle(SetTyping), toggler(self.reverse) .label("Обратный тест") - .on_toggle(DictionaryMessage::SetReverse), + .on_toggle(SetReverse), button(text!("Тест").center().width(Length::Fill)) .style(jl_button) .on_press(Test) @@ -424,7 +443,7 @@ impl DictionaryState { let mut col = Column::new().width(Length::Fill); col = col.push( button("Сбросить") - .on_press(DictionaryMessage::ResetTags) + .on_press(ResetTags) .style(|x: &Theme, _status| Style { background: None, text_color: x.palette().primary, @@ -439,7 +458,7 @@ impl DictionaryState { col = col.push( checkbox(*tag.1) .label(tag.0) - .on_toggle(|x1| DictionaryMessage::IncludeTag(tag.0.clone(), x1)), + .on_toggle(|x1| IncludeTag(tag.0.clone(), x1)), ) } @@ -508,7 +527,7 @@ impl DictionaryState { row = row.push( button("+") .style(text) - .on_press(DictionaryMessage::CreateGroup), + .on_press(CreateGroup), ); let state = &self.state.lock().unwrap(); @@ -519,7 +538,7 @@ impl DictionaryState { row = row.push( button(text!("{}", group.name.clone())) .style(text) - .on_press(DictionaryMessage::SelectGroup(index)), + .on_press(SelectGroup(index)), ); index = index + 1; } @@ -560,12 +579,12 @@ pub fn app_data_dir() -> PathBuf { dir } -pub fn app_cache_dir() -> PathBuf { - let mut dir = dirs::cache_dir().unwrap(); - dir.push("jap_learn"); - if !dir.exists() { - fs::create_dir(dir.clone()).unwrap(); - } - - dir -} +// pub fn app_cache_dir() -> PathBuf { +// let mut dir = dirs::cache_dir().unwrap(); +// dir.push("jap_learn"); +// if !dir.exists() { +// fs::create_dir(dir.clone()).unwrap(); +// } +// +// dir +// } diff --git a/src/dictionary_test.rs b/src/dictionary_test.rs index 5144ed7..bf95585 100644 --- a/src/dictionary_test.rs +++ b/src/dictionary_test.rs @@ -7,6 +7,7 @@ use iced::widget::{button, container, row, space, text, text_input, Row}; use iced::Background::Color; use iced::{alignment, Border, Element, Fill, Task, Theme}; use rand::prelude::SliceRandom; +use crate::dictionary_test::DictionaryQuizMessage::*; use crate::lang::WordData; use crate::navigation::{NavigatedPage, Page}; use crate::navigation::Page::PreviousPage; @@ -36,7 +37,7 @@ pub enum DictionaryQuizMessage { impl NavigatedPage for DictionaryQuizState { fn navigate(&self, message: &DictionaryQuizMessage) -> Option { match message { - DictionaryQuizMessage::Back => Some(PreviousPage), + Back => Some(PreviousPage), _ => None, } } @@ -64,10 +65,10 @@ impl DictionaryQuizState { pub fn update(&mut self, message: DictionaryQuizMessage) -> Task { match message { - DictionaryQuizMessage::Back => {} - DictionaryQuizMessage::AnswerChanged(c) => self.answer = c.clone(), - DictionaryQuizMessage::SubmitAnswer => self.submit(), - DictionaryQuizMessage::Appeal => self.appeal_answer(), + Back => {} + AnswerChanged(c) => self.answer = c.clone(), + SubmitAnswer => self.submit(), + Appeal => self.appeal_answer(), } Task::none() } @@ -92,8 +93,8 @@ impl DictionaryQuizState { text_input("Перевод", &self.answer) .size(28) .width(250) - .on_input(DictionaryQuizMessage::AnswerChanged) - .on_submit(DictionaryQuizMessage::SubmitAnswer), + .on_input(AnswerChanged) + .on_submit(SubmitAnswer), row![ text!("{}", self.score.total.to_string()).size(25), text!("{}", self.score.correct.to_string()) @@ -105,7 +106,7 @@ impl DictionaryQuizState { ] .spacing(DEFAULT_SPACING), row![ - button("Закончить").style(jl_button).on_press(DictionaryQuizMessage::Back), + button("Закончить").style(jl_button).on_press(Back), self.appeal_button() ] .spacing(DEFAULT_SPACING), @@ -206,7 +207,7 @@ impl DictionaryQuizState { fn appeal_button(&self) -> Element<'_, DictionaryQuizMessage> { if self.is_help && self.no_typing == false { return button("Апелляция").style(jl_button) - .on_press(DictionaryQuizMessage::Appeal) + .on_press(Appeal) .into(); } space().into() diff --git a/src/history.rs b/src/history.rs index 8d9fab0..62a173b 100644 --- a/src/history.rs +++ b/src/history.rs @@ -1,18 +1,17 @@ -use crate::data_provider::history::{HistoryItem, get_history_of_set}; +use crate::data_provider::history::{get_history_of_set, HistoryItem}; use crate::lang::WordData; +use crate::navigation::{NavigatedPage, Page}; +use crate::styling::*; use crate::{AppState, RootMessage}; -use HistoryMessage::Back; use iced::alignment::Horizontal::Center; use iced::widget::space::horizontal; use iced::widget::*; -use iced::{Element, Fill, FillPortion, Left, Task}; +use iced::{Element, Fill, FillPortion, Task}; use std::sync::{Arc, Mutex}; -use crate::navigation::{NavigatedPage, Page}; -use crate::styling::*; +use HistoryMessage::Back; #[derive(Clone)] pub struct HistoryState { - set_id: u32, list: Vec, words: Vec, } @@ -21,7 +20,6 @@ impl NavigatedPage for HistoryState { fn navigate(&self, message: &HistoryMessage) -> Option { match message { Back => Some(Page::PreviousPage), - _ => None, } } } @@ -43,7 +41,6 @@ impl HistoryState { .collect(); Self { - set_id: id, list: history, words, } @@ -54,25 +51,26 @@ impl HistoryState { } pub fn view(&self) -> Element<'_, HistoryMessage> { - container( - iced::widget::column![ - button("Назад").style(jl_button).on_press(Back), - iced::widget::row![ + back_overlay(iced::widget::row![ horizontal().width(FillPortion(1)), scrollable(self.history_lines().padding(DEFAULT_SPACING)) .height(Fill) .width(FillPortion(5)), horizontal().width(FillPortion(1)) ] - .height(Fill) - .width(Fill) - ] - .align_x(Left) - .width(Fill), - ) - .center_x(Fill) - .padding(10) - .into() + .height(Fill) + .width(Fill).into(), Back) + // container( + // iced::widget::column![ + // button("Назад").style(jl_button).on_press(Back), + // + // ] + // .align_x(Left) + // .width(Fill), + // ) + // .center_x(Fill) + // .padding(10) + // .into() } fn history_lines(&self) -> Column<'_, HistoryMessage> { diff --git a/src/lang.rs b/src/lang.rs index 861e10a..3688f22 100644 --- a/src/lang.rs +++ b/src/lang.rs @@ -298,7 +298,7 @@ impl CardStatistics { let time = Utc::now() - self.last_open; let days = time.num_days(); let multiplier = FADE_PER_DAY.powi(days as i32); - self.score as f32 * multiplier + (self.score as f32 * multiplier).max(1.0) } } @@ -574,17 +574,30 @@ impl SemiRandomSRSModule { #[derive(Clone)] struct WorstWordsSRSModule { initialized: bool, - rounds: u8, - pool: Vec + pool_size: usize, + rounds_remaining: u8, + pool: Vec, + queue: Vec, + rounds_count: u8, } impl SRSModule for WorstWordsSRSModule { - fn next(&mut self, _: &mut CardSet) -> usize { - todo!() + fn next(&mut self, set: &mut CardSet) -> usize { + if self.rounds_remaining == 0 { + self.fill_pool(set); + self.rounds_remaining = self.rounds_count; + } + + if self.queue.is_empty() { + self.queue.append(&mut self.pool.clone()); + self.rounds_remaining -= 1; + } + + self.queue.pop().unwrap() } + fn open(&mut self, _: WordOpenMode, _: usize, _: CardStatistics) { - todo!() } fn init(&mut self, _: &mut CardSet) { @@ -596,8 +609,19 @@ impl WorstWordsSRSModule { fn new() -> WorstWordsSRSModule { WorstWordsSRSModule { initialized: false, - rounds: 0, + pool_size: 15, + rounds_count: 2, + rounds_remaining: 0, pool: vec![], + queue: vec![], } } + + fn fill_pool(&mut self, set: &CardSet) { + let mut sorted = set.set.clone().into_iter().zip(0..set.set.len()).collect::>(); + sorted.sort_by_key(|c| c.0.score); + let mut worst = sorted[0..self.pool_size].iter().map(|(_, index)| *index).collect::>(); + worst.shuffle(&mut rand::rng()); + self.pool = worst; + } } diff --git a/src/main.rs b/src/main.rs index 93e11f5..1e5f17f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,9 +24,8 @@ use crate::navigation::{AppSettings, RootMessage, ScreenState}; use crate::quiz::*; use crate::repetitions::CardSetSettings; use crate::RootMessage::Keyboard; -use fontdb::Database; -use iced::{keyboard, Program, Subscription, Theme}; use iced::Font; +use iced::{keyboard, Program, Subscription, Theme}; use rusqlite::Connection; diff --git a/src/quiz.rs b/src/quiz.rs index 0c3b369..baf0ee8 100644 --- a/src/quiz.rs +++ b/src/quiz.rs @@ -5,6 +5,7 @@ use iced::{alignment, Element, Fill, Task}; use rand::seq::SliceRandom; use crate::navigation::{NavigatedPage, Page}; use crate::navigation::Page::PreviousPage; +use crate::quiz::QuizMessage::*; use crate::styling::*; #[derive(Clone, Debug)] @@ -20,7 +21,7 @@ pub struct QuizState { impl NavigatedPage for QuizState { fn navigate(&self, message: &QuizMessage) -> Option { - if let QuizMessage::Back = message { + if let Back = message { Some(PreviousPage) } else { None @@ -55,7 +56,7 @@ impl Default for QuizState { impl QuizState { pub fn update(&mut self, message: QuizMessage) -> Task { match message { - QuizMessage::ContentChanged(content) => { + ContentChanged(content) => { if content.contains("`") { self.is_help = true; self.score.fail += 1; @@ -77,7 +78,7 @@ impl QuizState { self.update_showed() } } - QuizMessage::Back => todo!(), + Back => todo!(), } Task::none() } @@ -118,7 +119,7 @@ impl QuizState { text_input("Романдзи", &self.current_roman) .size(28) .width(150) - .on_input(QuizMessage::ContentChanged), + .on_input(ContentChanged), row![ text!("{}", self.score.total.to_string()).size(25), text!("{}", self.score.correct.to_string()) @@ -129,7 +130,7 @@ impl QuizState { .size(25), ] .spacing(DEFAULT_SPACING), - button("Закончить").style(jl_button).on_press(QuizMessage::Back), + button("Закончить").style(jl_button).on_press(Back), ] .spacing(DEFAULT_SPACING) .align_x(alignment::Horizontal::Center), diff --git a/src/randomizer.rs b/src/randomizer.rs index 69f20eb..5300412 100644 --- a/src/randomizer.rs +++ b/src/randomizer.rs @@ -1,17 +1,16 @@ - pub mod randomizer { - use crate::randomizer::randomizer::RandomizerMessage::{Back, Start, Edit}; - use crate::{RootMessage}; - use iced::widget::{button, container, text_editor}; + use crate::navigation::{NavigatedPage, Page}; + use crate::randomizer::randomizer::RandomizerMessage::{Back, Edit, Start}; + use crate::styling::{back_overlay, jl_button}; + use crate::RootMessage; + use iced::widget::{button, text_editor}; use iced::Task; use rand::prelude::SliceRandom; - use crate::navigation::{NavigatedPage, Page}; - use crate::styling::jl_button; #[derive(Clone, Debug)] pub struct RandomizerState { text: text_editor::Content, - list: Vec + list: Vec, } #[derive(Debug, Clone)] @@ -38,15 +37,23 @@ pub mod randomizer { impl RandomizerState { pub fn new() -> RandomizerState { - RandomizerState { text: Default::default(), list: vec![] } + RandomizerState { + text: Default::default(), + list: vec![], + } } pub fn update(&mut self, message: RandomizerMessage) -> Task { match message { Edit(action) => { self.text.perform(action); - self.list = self.text.text().split("\n").map(|s| s.to_string()).collect(); - }, + self.list = self + .text + .text() + .split("\n") + .map(|s| s.to_string()) + .collect(); + } Start => { self.list.shuffle(&mut rand::rng()); self.text = text_editor::Content::with_text(self.list.join("\n").as_str()); @@ -57,17 +64,19 @@ pub mod randomizer { } pub fn view(&self) -> iced::Element<'_, RandomizerMessage> { - container( - iced::widget::column![ - button("Назад").style(jl_button).on_press(Back), - text_editor(&self.text).on_action(Edit - ).width(400).height(400).placeholder("Каждый элемент с новой строки"), - button("Перемешать").style(jl_button).on_press(Start), - ] - .spacing(5), + back_overlay( + iced::widget::column![ + text_editor(&self.text) + .on_action(Edit) + .width(400) + .height(400) + .placeholder("Каждый элемент с новой строки"), + button("Перемешать").style(jl_button).on_press(Start), + ] + .spacing(5) + .into(), + Back, ) - .padding(10) - .into() } } } diff --git a/src/repetition.rs b/src/repetition.rs index 51f3518..e4373e4 100644 --- a/src/repetition.rs +++ b/src/repetition.rs @@ -8,8 +8,8 @@ use crate::{AppState, RootMessage}; use iced::alignment::Horizontal::Center; use iced::keyboard::key::Physical::Code; use iced::widget::text::Style; -use iced::widget::{Column, button, column, container, row, rule, space, text, tooltip}; -use iced::{Element, Fill, Left, Task, Theme, alignment, keyboard}; +use iced::widget::{button, column, container, row, rule, space, text, tooltip, Column}; +use iced::{alignment, keyboard, Element, Fill, Task, Theme}; use rodio::MixerDeviceSink; use std::collections::HashSet; use std::sync::{Arc, Mutex}; @@ -113,12 +113,7 @@ impl RepetitionState { } pub fn view(&self) -> Element<'_, RepetitionMessage> { - container( - iced::widget::column![ - button("Назад") - .style(jl_button) - .on_press(RepetitionMessage::Back), - column![ + back_overlay(column![ container(self.draw_forward()) .width(Fill) .height(Fill) @@ -141,15 +136,8 @@ impl RepetitionState { / 100.0 ) ] - .height(Fill) - .width(Fill) - ] - .align_x(Left) - .width(Fill), - ) - .center_x(Fill) - .padding(10) - .into() + .height(Fill) + .width(Fill).into(), RepetitionMessage::Back) } fn draw_forward(&self) -> Element<'_, RepetitionMessage> { diff --git a/src/repetitions.rs b/src/repetitions.rs index 1348975..53c0a65 100644 --- a/src/repetitions.rs +++ b/src/repetitions.rs @@ -11,7 +11,7 @@ use iced::widget::button::{danger, Status}; pub use iced::widget::button::{Catalog, Style}; use iced::widget::container::bordered_box; use iced::widget::{button, column, container, radio, row, scrollable, space, text, text_input, Column}; -use iced::{Background, Border, Center, Color, Element, Fill, Left, Length, Shadow, Task, Theme}; +use iced::{Background, Border, Center, Color, Element, Fill, Length, Shadow, Task, Theme}; use rhai::{Engine, Scope}; use std::sync::{Arc, Mutex}; @@ -120,10 +120,7 @@ impl RepetitionsState { } pub fn view(&self) -> Element<'_, RepetitionsMessage> { - container( - iced::widget::column![ - button("Назад").style(jl_button).on_press(RepetitionsMessage::Back), - row![ + back_overlay(row![ column![ scrollable(self.sets_list()).height(Fill), button("Добавить").style(jl_button) @@ -135,17 +132,10 @@ impl RepetitionsState { self.selected_set_view(), self.launch_button() ] - .align_y(Center) - .spacing(DEFAULT_SPACING) - .width(Fill) - .height(Fill) - ] - .align_x(Left) - .width(Fill), - ) - .center_x(Fill) - .padding(10) - .into() + .align_y(Center) + .spacing(DEFAULT_SPACING) + .width(Fill) + .height(Fill).into(), RepetitionsMessage::Back) } fn launch_button(&self) -> Element<'_, RepetitionsMessage> { @@ -223,7 +213,7 @@ impl RepetitionsState { fn count_view(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> { if let Some(count) = set.count { - return text!("Колличество слов: {}", count).into(); + return text!("Количество слов: {}", count).into(); } space().into() } diff --git a/src/selector.rs b/src/selector.rs index 3f0402e..8f8474e 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -9,7 +9,7 @@ use crate::{AppState, QuizState, RootMessage}; use iced::widget::*; use iced::{alignment, Element, Task}; use crate::navigation::{NavigatedPage, Page}; -use crate::navigation::Page::{Quiz, Writing}; +use crate::navigation::Page::*; use crate::styling::*; use crate::sync::SyncState; @@ -44,16 +44,16 @@ impl NavigatedPage for SelectorState { }; } if let SelectorMessage::ToDictionary = message { - return Some(Page::Dictionary(DictionaryState::new(self.state.clone()))); + return Some(Dictionary(DictionaryState::new(self.state.clone()))); } if let SelectorMessage::ToRandomize = message { - return Some(Page::Randomizer(RandomizerState::default())); + return Some(Randomizer(RandomizerState::default())); } if let SelectorMessage::ToRepetitions = message { - return Some(Page::Repetitions(RepetitionsState::new(self.state.clone()))); + return Some(Repetitions(RepetitionsState::new(self.state.clone()))); } if let SelectorMessage::ToSync = message { - return Some(Page::Sync(SyncState::new(self.state.clone()))); + return Some(Sync(SyncState::new(self.state.clone()))); } None } diff --git a/src/styling.rs b/src/styling.rs index c8cdcde..2fd5020 100644 --- a/src/styling.rs +++ b/src/styling.rs @@ -1,11 +1,11 @@ +use crate::repetitions::Style; use iced::border::Radius; -use iced::Element; use iced::widget::button::{primary, Status}; use iced::widget::{button, container}; -use crate::repetitions::Style; -use iced_core::{Border, Theme}; +use iced::Element; use iced_core::alignment::Horizontal::Left; use iced_core::Length::Fill; +use iced_core::{Border, Theme}; pub const DEFAULT_SPACING: f32 = 16.0; pub const ACCENT_FONT_SIZE: f32 = 22.0; @@ -13,8 +13,7 @@ const BUTTON_RADIUS: f32 = 8.0; pub fn jl_button(theme: &Theme, status: Status) -> Style { let mut style = primary(theme, status); - style.border = Border - { + style.border = Border { color: Default::default(), width: 0.0, radius: Radius::new(BUTTON_RADIUS), @@ -25,15 +24,14 @@ pub fn jl_button(theme: &Theme, status: Status) -> Style { pub fn back_overlay<'a, T: Clone + 'a>(content: Element<'a, T>, back_message: T) -> Element<'a, T> { container( iced::widget::column![ - button("Назад") - .style(jl_button) - .on_press(back_message), + button("Назад").style(jl_button).on_press(back_message), content - ] - .align_x(Left) - .width(Fill), + ] + .spacing(5) + .align_x(Left) + .width(Fill), ) - .center_x(Fill) - .padding(10) - .into() -} \ No newline at end of file + .center_x(Fill) + .padding(10) + .into() +} diff --git a/src/sync.rs b/src/sync.rs index 845a601..6baeea8 100644 --- a/src/sync.rs +++ b/src/sync.rs @@ -3,12 +3,12 @@ use crate::dictionary::app_data_dir; use crate::navigation::Page::PreviousPage; use crate::navigation::{NavigatedPage, Page}; use crate::styling::*; -use crate::sync::SyncMessage::NextAnimation; +use crate::sync::SyncMessage::*; use crate::{fill_state, AppState, RootMessage}; use iced::widget::button::danger; use iced::widget::container::rounded_box; use iced::widget::{button, column, container, progress_bar, row, space, text}; -use iced::{Center, Element, Fill, Font, Left, Length, Task}; +use iced::{Center, Element, Fill, Font, Length, Task}; use std::io; use std::sync::{Arc, Mutex}; use std::time::Duration; @@ -41,7 +41,7 @@ pub struct SyncState { impl NavigatedPage for SyncState { fn navigate(&self, message: &SyncMessage) -> Option { - if let SyncMessage::Back = message + if let Back = message && !self.frozen { Some(PreviousPage) @@ -62,25 +62,25 @@ impl SyncState { pub fn update(&mut self, message: SyncMessage) -> Task { match message { - SyncMessage::Back => {} - SyncMessage::CopyKey => { + Back => {} + CopyKey => { let state = self.state.lock().unwrap(); let key = state.sync_data.key.clone().unwrap(); return iced::clipboard::write(key) - .map(|_val: String| RootMessage::Sync(SyncMessage::KeyCopied)); + .map(|_val: String| RootMessage::Sync(KeyCopied)); } - SyncMessage::InitSync => { + InitSync => { return Task::perform(first_sync(), |id| { - RootMessage::Sync(SyncMessage::IdReceived(id)) + RootMessage::Sync(IdReceived(id)) }); } - SyncMessage::GetKey => { + GetKey => { return iced::clipboard::read().map(|key| { - RootMessage::Sync(SyncMessage::IdReceived(key.unwrap_or_else(String::new))) + RootMessage::Sync(IdReceived(key.unwrap_or_else(String::new))) }); } - SyncMessage::KeyCopied => {} - SyncMessage::IdReceived(new_id) => { + KeyCopied => {} + IdReceived(new_id) => { if validate_id(&new_id) == false { return Task::none(); } @@ -88,7 +88,7 @@ impl SyncState { state.sync_data.key = Some(new_id.clone()); set_setting("SYNC_KEY".to_string(), new_id, &state.connection); } - SyncMessage::Send => { + Send => { self.prepare_to_db_interaction(); let id = self.state.lock().unwrap().sync_data.key.clone().unwrap(); @@ -98,13 +98,13 @@ impl SyncState { |_| RootMessage::Sync(NextAnimation), ), Task::perform(send_data(id), |_| { - RootMessage::Sync(SyncMessage::NetworkFinished) + RootMessage::Sync(NetworkFinished) }), ]); return tasks; } - SyncMessage::GetLast => { + GetLast => { let id = self.state.lock().unwrap().sync_data.key.clone().unwrap(); let tasks = Task::batch([ Task::perform( @@ -112,7 +112,7 @@ impl SyncState { |_| RootMessage::Sync(NextAnimation), ), Task::perform(load_data(id), |_| { - RootMessage::Sync(SyncMessage::NetworkFinished) + RootMessage::Sync(NetworkFinished) }), ]); @@ -130,7 +130,7 @@ impl SyncState { ); } } - SyncMessage::NetworkFinished => { + NetworkFinished => { self.frozen = false; let mut updated_state = AppState::new(); fill_state(&mut updated_state); @@ -143,8 +143,8 @@ impl SyncState { state.word_groups = updated_state.word_groups; } - SyncMessage::Disable => {} - SyncMessage::DisableSync => { + Disable => {} + DisableSync => { let mut state = self.state.lock().unwrap(); state.sync_data.key = None; @@ -155,21 +155,9 @@ impl SyncState { } pub fn view(&self) -> Element<'_, SyncMessage> { - container( - column![ - button("Назад").style(jl_button).on_press(SyncMessage::Back), - row![space().width(Fill), self.sync_column(), space().width(Fill)] - .spacing(DEFAULT_SPACING) - .width(Fill) - ] - .align_x(Left) - .width(Fill) - .height(Fill) - .spacing(DEFAULT_SPACING), - ) - .center_x(Fill) - .padding(10) - .into() + back_overlay(row![space().width(Fill), self.sync_column(), space().width(Fill)] + .spacing(DEFAULT_SPACING) + .width(Fill).into(), Back) } fn sync_column(&self) -> Element<'_, SyncMessage> { @@ -188,23 +176,23 @@ impl SyncState { .style(rounded_box), button("Скопировать в буфер обмена").style(jl_button) - .on_press(SyncMessage::CopyKey) + .on_press(CopyKey) .width(Fill), row![ - button("↑ Отправить").style(jl_button).on_press(SyncMessage::Send), + button("↑ Отправить").style(jl_button).on_press(Send), space().width(Fill), - button("↓ Скачать").style(jl_button).on_press(SyncMessage::GetLast) + button("↓ Скачать").style(jl_button).on_press(GetLast) ] .spacing(DEFAULT_SPACING), container(network_view).width(Fill), button("Отключить синхронизацию") .style(danger) - .on_press(SyncMessage::DisableSync), + .on_press(DisableSync), ] } else { column![ - button("Создать сохранение").style(jl_button).on_press(SyncMessage::InitSync), - button("Вставить ключ из буфера").style(jl_button).on_press(SyncMessage::GetKey), + button("Создать сохранение").style(jl_button).on_press(InitSync), + button("Вставить ключ из буфера").style(jl_button).on_press(GetKey), ] } } diff --git a/src/word.rs b/src/word.rs index 2a6c539..a98163a 100644 --- a/src/word.rs +++ b/src/word.rs @@ -1,13 +1,14 @@ use crate::data_provider::words::{delete_word, update_word}; use crate::lang::WordData; +use crate::navigation::Page::PreviousPage; +use crate::navigation::{NavigatedPage, Page}; +use crate::styling::*; +use crate::word::WordMessage::*; use crate::{AppState, RootMessage}; use iced::widget::button::danger; -use iced::widget::{button, column, container, row, rule, scrollable, space, text, text_input}; +use iced::widget::{button, column, row, rule, scrollable, space, text, text_input}; use iced::{Element, Fill, Task}; use std::sync::{Arc, Mutex}; -use crate::navigation::{NavigatedPage, Page}; -use crate::navigation::Page::PreviousPage; -use crate::styling::*; #[derive(Clone)] pub struct WordState { @@ -18,7 +19,7 @@ pub struct WordState { impl NavigatedPage for WordState { fn navigate(&self, message: &WordMessage) -> Option { - if let WordMessage::Back = message { + if let Back = message { Some(PreviousPage) } else { None @@ -35,34 +36,37 @@ impl WordState { impl WordState { pub fn update(&mut self, message: WordMessage) -> Task { match message { - WordMessage::Back => {} - WordMessage::Save => { + Back => {} + Save => { let mut state = self.state.lock().unwrap(); state.dictionary[self.index] = self.word.clone(); update_word(&mut self.word, &state.connection); - return Task::done(RootMessage::Word(WordMessage::Back)); + return Task::done(RootMessage::Word(Back)); } - WordMessage::Delete => { + Delete => { let mut state = self.state.lock().unwrap(); state.dictionary.remove(self.index); delete_word(&self.word, &state.connection); - return Task::done(RootMessage::Word(WordMessage::Back)); + return Task::done(RootMessage::Word(Back)); } - WordMessage::SetTags(n) => self.word.tags = n, - WordMessage::SetKey(n) => { + SetTags(n) => self.word.tags = n, + SetKey(n) => { self.word.key = n; } - WordMessage::SetValue(n) => { + SetValue(n) => { self.word.value = n; } - WordMessage::SetAdditional(key, value) => match key.as_str() { + SetAdditional(key, value) => match key.as_str() { _ => { self.word.additional.insert(key, value.clone()); } }, - WordMessage::AddAdditional(key) => { + AddAdditional(key) => { self.word.additional.insert(key, "".to_string()); } + RemoveAdditional(key) => { + self.word.additional.remove(key.as_str()); + } } Task::none() } @@ -73,14 +77,14 @@ impl WordState { fast_add = fast_add.push( button("Чтение") .style(button::text) - .on_press(WordMessage::AddAdditional("reading".to_string())), + .on_press(AddAdditional("reading".to_string())), ); } if !self.word.additional.contains_key("description") { fast_add = fast_add.push( button("Описание") .style(button::text) - .on_press(WordMessage::AddAdditional("description".to_string())), + .on_press(AddAdditional("description".to_string())), ); } @@ -88,18 +92,26 @@ impl WordState { fast_add = fast_add.push( button("В контексте") .style(button::text) - .on_press(WordMessage::AddAdditional("context".to_string())), + .on_press(AddAdditional("context".to_string())), ); } - + let mut col = iced::widget::column![ - button("Назад").style(jl_button).on_press(WordMessage::Back), - text!("Ключ"), - text_input("key", &self.word.key).on_input(WordMessage::SetKey), - text!("Значение"), - text_input("value", &self.word.value).on_input(WordMessage::SetValue), - text!("Теги"), - text_input("tags", &self.word.tags).on_input(WordMessage::SetTags), + column![ + text!("Ключ"), + text_input("key", &self.word.key).on_input(SetKey), + ] + .spacing(DEFAULT_SPACING / 4.0), + column![ + text!("Значение"), + text_input("value", &self.word.value).on_input(SetValue), + ] + .spacing(DEFAULT_SPACING / 4.0), + column![ + text!("Теги"), + text_input("tags", &self.word.tags).on_input(SetTags), + ] + .spacing(DEFAULT_SPACING / 4.0), rule::horizontal(2), scrollable(fast_add), ]; @@ -107,21 +119,20 @@ impl WordState { for more in &self.word.additional { col = col.push(self.get_view_for_more(more)); } - container( + + back_overlay( column![ - col.spacing(DEFAULT_SPACING).width(Fill).height(Fill), + col.spacing(DEFAULT_SPACING / 2.0).width(Fill).height(Fill), row![ - button("Сохранить").style(jl_button).on_press(WordMessage::Save), - button("Удалить") - .style(danger) - .on_press(WordMessage::Delete), + button("Сохранить").style(jl_button).on_press(Save), + button("Удалить").style(danger).on_press(Delete), ] .spacing(DEFAULT_SPACING) ] - .spacing(DEFAULT_SPACING), + .spacing(DEFAULT_SPACING) + .into(), + Back, ) - .padding(DEFAULT_SPACING) - .into() } fn get_view_for_more(&self, value: (&String, &String)) -> Element<'_, WordMessage> { @@ -140,19 +151,34 @@ impl WordState { fn description_field(&self, value: (&String, &String)) -> Element<'_, WordMessage> { self.additional_field(value, "Описание".to_string(), "description".to_string()) } - + fn context_field(&self, value: (&String, &String)) -> Element<'_, WordMessage> { self.additional_field(value, "В контексте".to_string(), "context".to_string()) } - - fn additional_field(&self, value: (&String, &String), name: String, id: String) -> Element<'_, WordMessage> { + + fn additional_field( + &self, + value: (&String, &String), + name: String, + id: String, + ) -> Element<'_, WordMessage> { column![ text!("{}", name), - text_input(id.clone().as_str(), &value.1) - .on_input(move |string| WordMessage::SetAdditional(id.clone(), string)) + row![ + text_input(id.clone().as_str(), &value.1) + .on_input({ + let value = id.clone(); + move |string| SetAdditional(value.clone(), string) + }) + .width(Fill), + button("-") + .on_press(RemoveAdditional(id.clone())) + .style(danger), + ] + .spacing(DEFAULT_SPACING / 4.0), ] - .spacing(DEFAULT_SPACING) - .into() + .spacing(DEFAULT_SPACING / 4.0) + .into() } } #[derive(Debug, Clone)] @@ -164,5 +190,6 @@ pub enum WordMessage { SetKey(String), SetValue(String), AddAdditional(String), + RemoveAdditional(String), SetAdditional(String, String), }