repetition works
This commit is contained in:
@@ -1,10 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use crate::lang::CardStatistics;
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
use iced::time::now;
|
|
||||||
use rusqlite::{Connection, ToSql};
|
|
||||||
use rusqlite::types::ToSqlOutput;
|
|
||||||
use crate::lang::{CardSet, CardStatistics, DictionaryElement};
|
|
||||||
use crate::repetitions::CardSetSettings;
|
use crate::repetitions::CardSetSettings;
|
||||||
|
use rusqlite::Connection;
|
||||||
|
|
||||||
pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec<CardStatistics> {
|
pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec<CardStatistics> {
|
||||||
let mut stmt = connection.prepare("SELECT id, word_id, score, last_opened FROM card_stats WHERE set_id = ?1").unwrap();
|
let mut stmt = connection.prepare("SELECT id, word_id, score, last_opened FROM card_stats WHERE set_id = ?1").unwrap();
|
||||||
@@ -12,8 +8,9 @@ pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec<
|
|||||||
Ok(CardStatistics {
|
Ok(CardStatistics {
|
||||||
id: row.get(0)?,
|
id: row.get(0)?,
|
||||||
word_id: row.get(1)?,
|
word_id: row.get(1)?,
|
||||||
|
set_id: set.id,
|
||||||
score: row.get(2)?,
|
score: row.get(2)?,
|
||||||
last_open: row.get(2)?,
|
last_open: row.get(3)?,
|
||||||
})
|
})
|
||||||
}).unwrap();
|
}).unwrap();
|
||||||
|
|
||||||
@@ -25,50 +22,64 @@ pub fn load_stats_of_set(set: &CardSetSettings, connection: &Connection) -> Vec<
|
|||||||
buffer
|
buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_set(set: &mut CardSetSettings, connection: &Connection) {
|
pub fn add_stat(stat: &mut CardStatistics, connection: &Connection) {
|
||||||
let index = connection
|
let index = connection
|
||||||
.query_row(
|
.query_row(
|
||||||
"INSERT INTO card_set (name, forward, backward, filter) VALUES (?1, ?2, ?3, ?4) RETURNING id",
|
"INSERT INTO card_stats (word_id, set_id, score, last_opened) VALUES (?1, ?2, ?3, ?4) RETURNING id",
|
||||||
(
|
(
|
||||||
&set.name,
|
&stat.word_id,
|
||||||
&set.forward,
|
&stat.set_id,
|
||||||
&set.backward,
|
&stat.score,
|
||||||
&set.filter,
|
&stat.last_open,
|
||||||
),
|
),
|
||||||
|row| row.get(0)
|
|row| row.get(0)
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|e| {println!("{}", e); 0});
|
.unwrap_or_else(|e| {println!("{}", e); 0});
|
||||||
|
|
||||||
set.id = index;
|
stat.id = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_card_set(set: &mut CardSetSettings, connection: &Connection){
|
pub fn update_stat(stat: &mut CardStatistics, connection: &Connection){
|
||||||
if set.id == 0 {
|
if stat.id == 0 {
|
||||||
add_set(set, &connection);
|
add_stat(stat, &connection);
|
||||||
}
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
connection
|
connection
|
||||||
.execute(
|
.execute(
|
||||||
"UPDATE card_set SET name = ?1, forward = ?2, backward = ?3, filter = ?4 WHERE id = ?5",
|
"UPDATE card_stats SET word_id = ?1, set_id = ?2, score = ?3, last_opened = ?4 WHERE id = ?5",
|
||||||
(
|
(
|
||||||
&set.name,
|
&stat.word_id,
|
||||||
&set.forward,
|
&stat.set_id,
|
||||||
&set.backward,
|
&stat.score,
|
||||||
&set.filter,
|
&stat.last_open,
|
||||||
&set.id
|
&stat.id
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.unwrap_or_else(|e| {println!("{}", e); 0});
|
.unwrap_or_else(|e| {println!("{}", e); 0});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn update_stat_score(stat: &CardStatistics, connection: &Connection){
|
||||||
|
connection
|
||||||
|
.execute(
|
||||||
|
"UPDATE card_stats SET score = ?1, last_opened = ?2 WHERE id = ?3",
|
||||||
|
(
|
||||||
|
&stat.score,
|
||||||
|
&stat.last_open,
|
||||||
|
&stat.id
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|e| {println!("{}", e); 0});
|
||||||
|
}
|
||||||
|
|
||||||
pub fn delete_set(set: &CardSetSettings, connection: &Connection) {
|
pub fn delete_set(set: &CardSetSettings, connection: &Connection) {
|
||||||
if set.id == 0 {
|
if set.id == 0 {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
connection
|
connection
|
||||||
.execute("DELETE FROM card_set WHERE id = ?1", (&set.id,))
|
.execute("DELETE FROM card_stats WHERE id = ?1", (&set.id,))
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
println!("{}", e);
|
println!("{}", e);
|
||||||
0
|
0
|
||||||
|
|||||||
+64
-13
@@ -1,10 +1,14 @@
|
|||||||
|
use crate::data_provider::card_stats::{add_stat, load_stats_of_set, update_stat_score};
|
||||||
use crate::repetitions::CardSetSettings;
|
use crate::repetitions::CardSetSettings;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use rand::distr::weighted::WeightedIndex;
|
||||||
|
use rand::distr::Distribution;
|
||||||
|
use rand::rng;
|
||||||
|
use rand::rngs::ThreadRng;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use crate::data_provider::card_stats::load_stats_of_set;
|
|
||||||
|
|
||||||
const MAX_SCORE: u8 = 25_u8;
|
const MAX_SCORE: u8 = 25_u8;
|
||||||
const FADE_PER_DAY: f32 = 0.95;
|
const FADE_PER_DAY: f32 = 0.95;
|
||||||
@@ -242,29 +246,30 @@ impl DictionaryElement {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, PartialEq)]
|
||||||
pub struct CardStatistics {
|
pub struct CardStatistics {
|
||||||
pub id: u32,
|
pub id: u32,
|
||||||
pub word_id: u32,
|
pub word_id: u32,
|
||||||
|
pub set_id: u32,
|
||||||
pub last_open: DateTime<Utc>,
|
pub last_open: DateTime<Utc>,
|
||||||
pub score: u8,
|
pub score: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardStatistics {
|
impl CardStatistics {
|
||||||
pub fn open(&mut self, status: WordOpenMode) {
|
pub fn update(&mut self, status: WordOpenMode) {
|
||||||
self.last_open = Utc::now();
|
self.last_open = Utc::now();
|
||||||
match status {
|
match status {
|
||||||
WordOpenMode::Easy => {
|
WordOpenMode::Easy => {
|
||||||
self.score += 5;
|
self.score = self.calculated_score().round() as u8 + 5;
|
||||||
}
|
}
|
||||||
WordOpenMode::Ok => {
|
WordOpenMode::Ok => {
|
||||||
self.score += 3;
|
self.score += self.calculated_score().round() as u8 + 3;
|
||||||
}
|
}
|
||||||
WordOpenMode::Hard => {
|
WordOpenMode::Hard => {
|
||||||
self.score -= 1;
|
self.score = self.calculated_score().round() as u8 - 1;
|
||||||
}
|
}
|
||||||
WordOpenMode::None => {
|
WordOpenMode::None => {
|
||||||
self.score /= 2;
|
self.score = (self.calculated_score() * 0.5) as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -279,10 +284,11 @@ impl CardStatistics {
|
|||||||
let time = Utc::now() - self.last_open;
|
let time = Utc::now() - self.last_open;
|
||||||
let days = time.num_days();
|
let days = time.num_days();
|
||||||
let multiplier = FADE_PER_DAY.powi(days as i32);
|
let multiplier = FADE_PER_DAY.powi(days as i32);
|
||||||
self.score as f32 * multiplier
|
(1.0 / self.score as f32) * multiplier
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum WordOpenMode {
|
pub enum WordOpenMode {
|
||||||
Easy,
|
Easy,
|
||||||
Ok,
|
Ok,
|
||||||
@@ -292,22 +298,67 @@ pub enum WordOpenMode {
|
|||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct CardSet {
|
pub struct CardSet {
|
||||||
|
words: Vec<DictionaryElement>,
|
||||||
set: Vec<CardStatistics>,
|
set: Vec<CardStatistics>,
|
||||||
last_weights: Vec<f32>,
|
last_weights: WeightedIndex<f32>,
|
||||||
|
current_word_index: Option<usize>,
|
||||||
|
generator: ThreadRng,
|
||||||
|
state: Arc<Mutex<AppState>>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CardSet {
|
impl CardSet {
|
||||||
pub fn new(settings: CardSetSettings, state: Arc<Mutex<AppState>>) -> Self {
|
pub fn new(settings: &CardSetSettings, state: Arc<Mutex<AppState>>) -> Self {
|
||||||
|
let state_for = state.clone();
|
||||||
let state_locked = state.lock().unwrap();
|
let state_locked = state.lock().unwrap();
|
||||||
|
|
||||||
let current_set = load_stats_of_set(&settings, &state_locked.connection);
|
let mut current_set = load_stats_of_set(&settings, &state_locked.connection);
|
||||||
let last_list = settings.get_word_list(&state_locked);
|
let last_list = settings.get_word_list(&state_locked);
|
||||||
|
let saved_ids = current_set.iter().map(|l| l.word_id).collect::<Vec<u32>>();
|
||||||
|
for word in &last_list {
|
||||||
|
if !saved_ids.contains(&word.id) {
|
||||||
|
let mut new_statistic = CardStatistics {
|
||||||
|
id: 0,
|
||||||
|
word_id: word.id.clone(),
|
||||||
|
last_open: Utc::now(),
|
||||||
|
score: 1,
|
||||||
|
set_id: settings.id.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
let weights = current_set.iter().map(|x| x.calculated_score()).collect();
|
add_stat(&mut new_statistic, &state_locked.connection);
|
||||||
|
|
||||||
|
current_set.push(new_statistic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let indexes = WeightedIndex::new(current_set.iter().map(|s| 1.0 / s.score as f32)).unwrap();
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
set: current_set,
|
set: current_set,
|
||||||
last_weights: weights,
|
words: last_list,
|
||||||
|
last_weights: indexes,
|
||||||
|
current_word_index: None,
|
||||||
|
generator: rng(),
|
||||||
|
state: state_for
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn next(&mut self) -> DictionaryElement {
|
||||||
|
let index = self.last_weights.sample(&mut self.generator);
|
||||||
|
self.current_word_index = Some(index);
|
||||||
|
self.words[index].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn open(&mut self, status: WordOpenMode) {
|
||||||
|
if let None = self.current_word_index {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let word = &mut self.set[self.current_word_index.unwrap()];
|
||||||
|
word.update(status);
|
||||||
|
let new_weight = word.calculated_score();
|
||||||
|
self.last_weights.update_weights(&[(self.current_word_index.unwrap(), &new_weight)]).unwrap();
|
||||||
|
{
|
||||||
|
update_stat_score(word, &self.state.lock().unwrap().connection)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+30
-4
@@ -24,13 +24,15 @@ use crate::Page::{
|
|||||||
Dictionary, DictionaryQuiz, Quiz, Randomizer, Repetition, Repetitions, Selector, Writing,
|
Dictionary, DictionaryQuiz, Quiz, Randomizer, Repetition, Repetitions, Selector, Writing,
|
||||||
};
|
};
|
||||||
use iced::widget::text;
|
use iced::widget::text;
|
||||||
use iced::Element;
|
use iced::{keyboard, Element, Program, Subscription};
|
||||||
use iced::{Font, Task};
|
use iced::{Font, Task};
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use iced::keyboard::Event;
|
||||||
|
use crate::RootMessage::Keyboard;
|
||||||
|
|
||||||
fn main() -> iced::Result {
|
fn main() -> iced::Result {
|
||||||
iced::application(ScreenState::boot, ScreenState::update, ScreenState::view)
|
iced::application(ScreenState::boot, ScreenState::update, ScreenState::view).subscription(subscription)
|
||||||
.title("Kana learn app")
|
.title("Kana learn app")
|
||||||
.font(include_bytes!("../noto.ttf"))
|
.font(include_bytes!("../noto.ttf"))
|
||||||
.default_font(Font::with_name("Noto Sans JP"))
|
.default_font(Font::with_name("Noto Sans JP"))
|
||||||
@@ -38,7 +40,11 @@ fn main() -> iced::Result {
|
|||||||
run()
|
run()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
fn subscription(_state: &ScreenState) -> Subscription<RootMessage> {
|
||||||
|
keyboard::listen().map(|e| Keyboard(e))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum RootMessage {
|
pub enum RootMessage {
|
||||||
Selector(SelectorMessage),
|
Selector(SelectorMessage),
|
||||||
Quiz(QuizMessage),
|
Quiz(QuizMessage),
|
||||||
@@ -48,6 +54,7 @@ pub enum RootMessage {
|
|||||||
Randomizer(RandomizerMessage),
|
Randomizer(RandomizerMessage),
|
||||||
Repetitions(RepetitionsMessage),
|
Repetitions(RepetitionsMessage),
|
||||||
Repetition(RepetitionMessage),
|
Repetition(RepetitionMessage),
|
||||||
|
Keyboard(keyboard::Event),
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Page {
|
enum Page {
|
||||||
@@ -93,6 +100,10 @@ impl ScreenState {
|
|||||||
(ScreenState::default(), Task::none())
|
(ScreenState::default(), Task::none())
|
||||||
}
|
}
|
||||||
pub fn update(&mut self, message: RootMessage) -> Task<RootMessage> {
|
pub fn update(&mut self, message: RootMessage) -> Task<RootMessage> {
|
||||||
|
if let Keyboard(e) = message {
|
||||||
|
return self.handle_keyboard(e);
|
||||||
|
}
|
||||||
|
|
||||||
state_update!(
|
state_update!(
|
||||||
message,
|
message,
|
||||||
self.stack,
|
self.stack,
|
||||||
@@ -107,7 +118,6 @@ impl ScreenState {
|
|||||||
);
|
);
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view(&self) -> Element<'_, RootMessage> {
|
pub fn view(&self) -> Element<'_, RootMessage> {
|
||||||
view_navigation!(
|
view_navigation!(
|
||||||
self.stack,
|
self.stack,
|
||||||
@@ -121,7 +131,18 @@ impl ScreenState {
|
|||||||
Repetition
|
Repetition
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_keyboard(&mut self, message: Event) -> Task<RootMessage> {
|
||||||
|
let page = self.stack.last_mut().unwrap();
|
||||||
|
match page {
|
||||||
|
Repetition(page) => page.press(&message),
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! view_navigation {
|
macro_rules! view_navigation {
|
||||||
@@ -146,6 +167,7 @@ macro_rules! state_update {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)*
|
)*
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,3 +190,7 @@ macro_rules! message_navigation {
|
|||||||
trait NavigatedPage<T> {
|
trait NavigatedPage<T> {
|
||||||
fn navigate(&self, message: &T) -> Option<Page>;
|
fn navigate(&self, message: &T) -> Option<Page>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait KeyPressedPage {
|
||||||
|
fn press(&mut self, message: &keyboard::Event);
|
||||||
|
}
|
||||||
+142
-14
@@ -1,15 +1,22 @@
|
|||||||
use std::sync::{Arc, Mutex};
|
use crate::lang::{CardSet, DictionaryElement, WordOpenMode};
|
||||||
use crate::Page::PreviousPage;
|
|
||||||
use crate::{AppState, NavigatedPage, Page, RootMessage};
|
|
||||||
use iced::widget::{button, container};
|
|
||||||
use iced::{Element, Fill, Left, Task};
|
|
||||||
use crate::lang::CardSet;
|
|
||||||
use crate::repetitions::CardSetSettings;
|
use crate::repetitions::CardSetSettings;
|
||||||
|
use crate::Page::PreviousPage;
|
||||||
|
use crate::{AppState, KeyPressedPage, NavigatedPage, Page, RootMessage};
|
||||||
|
use iced::alignment::Horizontal::Center;
|
||||||
|
use iced::keyboard::key::Physical::Code;
|
||||||
|
use iced::widget::container::rounded_box;
|
||||||
|
use iced::widget::space::vertical;
|
||||||
|
use iced::widget::{button, column, container, row, space, text};
|
||||||
|
use iced::{alignment, keyboard, Element, Fill, Left, Task};
|
||||||
|
use rusqlite::fallible_iterator::FallibleIterator;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RepetitionState {
|
pub struct RepetitionState {
|
||||||
pub set: CardSetSettings,
|
pub settings: CardSetSettings,
|
||||||
|
pub set: CardSet,
|
||||||
pub state: Arc<Mutex<AppState>>,
|
pub state: Arc<Mutex<AppState>>,
|
||||||
|
current_word: DictionaryElement,
|
||||||
|
open: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl NavigatedPage<RepetitionMessage> for RepetitionState {
|
impl NavigatedPage<RepetitionMessage> for RepetitionState {
|
||||||
@@ -24,34 +31,155 @@ impl NavigatedPage<RepetitionMessage> for RepetitionState {
|
|||||||
|
|
||||||
impl RepetitionState {
|
impl RepetitionState {
|
||||||
pub(crate) fn new(set: CardSetSettings, state: Arc<Mutex<AppState>>) -> RepetitionState {
|
pub(crate) fn new(set: CardSetSettings, state: Arc<Mutex<AppState>>) -> RepetitionState {
|
||||||
|
let mut card_set = CardSet::new(&set, state.clone());
|
||||||
|
let word = card_set.next();
|
||||||
RepetitionState {
|
RepetitionState {
|
||||||
set,
|
settings: set,
|
||||||
state
|
set: card_set,
|
||||||
|
state,
|
||||||
|
current_word: word,
|
||||||
|
open: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RepetitionState {
|
impl RepetitionState {
|
||||||
pub fn update(&mut self, message: RepetitionMessage) -> Task<RootMessage> {
|
pub fn update(&mut self, message: RepetitionMessage) -> Task<RootMessage> {
|
||||||
|
match message {
|
||||||
|
RepetitionMessage::Back => {}
|
||||||
|
RepetitionMessage::Next => {self.next()}
|
||||||
|
RepetitionMessage::Answer(m) => {self.answer(m)}
|
||||||
|
}
|
||||||
|
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn next(&mut self) {
|
||||||
|
if self.open {
|
||||||
|
self.set.open(WordOpenMode::None);
|
||||||
|
self.current_word = self.set.next();
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
self.open = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn answer(&mut self, mode: WordOpenMode) {
|
||||||
|
if !self.open {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set.open(mode);
|
||||||
|
self.open = false;
|
||||||
|
self.current_word = self.set.next();
|
||||||
|
}
|
||||||
|
|
||||||
pub fn view(&self) -> Element<'_, RepetitionMessage> {
|
pub fn view(&self) -> Element<'_, RepetitionMessage> {
|
||||||
container(
|
container(
|
||||||
iced::widget::column![
|
iced::widget::column![
|
||||||
button("Назад").on_press(RepetitionMessage::Back),
|
button("Назад").on_press(RepetitionMessage::Back),
|
||||||
|
column![
|
||||||
].align_x(Left).width(Fill)
|
container(self.draw_forward())
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.align_x(Center)
|
||||||
|
.align_y(alignment::Vertical::Center),
|
||||||
|
container(vertical().height(5)).width(Fill).padding(5).style(rounded_box),
|
||||||
|
container(self.draw_backward())
|
||||||
|
.width(Fill)
|
||||||
|
.height(Fill)
|
||||||
|
.align_x(Center)
|
||||||
|
.align_y(alignment::Vertical::Center),
|
||||||
|
container(
|
||||||
|
self.answer_bar()
|
||||||
|
).width(Fill).align_x(Center).height(30)
|
||||||
|
]
|
||||||
|
.height(Fill)
|
||||||
|
.width(Fill)
|
||||||
|
]
|
||||||
|
.align_x(Left)
|
||||||
|
.width(Fill),
|
||||||
)
|
)
|
||||||
.center_x(Fill)
|
.center_x(Fill)
|
||||||
.padding(10)
|
.padding(10)
|
||||||
.into()
|
.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),
|
||||||
|
_ => space().into(),
|
||||||
}
|
}
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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),
|
||||||
|
_ => 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)),
|
||||||
|
]
|
||||||
|
.spacing(10)
|
||||||
|
.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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl KeyPressedPage for RepetitionState {
|
||||||
|
fn press(&mut self, message: &keyboard::Event) {
|
||||||
|
if let keyboard::Event::KeyPressed {
|
||||||
|
key: _,
|
||||||
|
modified_key: _,
|
||||||
|
physical_key: pk,
|
||||||
|
location: _,
|
||||||
|
modifiers: _,
|
||||||
|
text: _,
|
||||||
|
repeat: _,
|
||||||
|
} = message
|
||||||
|
{
|
||||||
|
if let Code(code) = pk {
|
||||||
|
match code {
|
||||||
|
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),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub enum RepetitionMessage {
|
pub enum RepetitionMessage {
|
||||||
Next,
|
Next,
|
||||||
Back,
|
Back,
|
||||||
SwitchShowMode(bool),
|
Answer(WordOpenMode),
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -24,7 +24,11 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
|
|||||||
}
|
}
|
||||||
else if let RepetitionsMessage::GoToRepetition = message {
|
else if let RepetitionsMessage::GoToRepetition = message {
|
||||||
let clone = self.state.clone();
|
let clone = self.state.clone();
|
||||||
Some(Repetition(RepetitionState::new(self.state.lock().unwrap().card_sets[self.selected_set.unwrap()].clone(), clone) ))
|
let card_set;
|
||||||
|
{
|
||||||
|
card_set = self.state.lock().unwrap().card_sets[self.selected_set.unwrap()].clone();
|
||||||
|
}
|
||||||
|
Some(Repetition(RepetitionState::new(card_set, clone) ))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@@ -212,6 +216,7 @@ impl RepetitionsState {
|
|||||||
column
|
column
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum RepetitionsMessage {
|
pub enum RepetitionsMessage {
|
||||||
Next,
|
Next,
|
||||||
|
|||||||
Reference in New Issue
Block a user