Add navigation to repetition page

This commit is contained in:
2026-04-13 22:25:58 +03:00
parent 8df6eae781
commit a1217f90ec
12 changed files with 674 additions and 121 deletions
+76
View File
@@ -0,0 +1,76 @@
use std::collections::HashMap;
use iced::time::now;
use rusqlite::Connection;
use crate::lang::{CardSet, DictionaryElement};
use crate::repetitions::CardSetSettings;
pub fn load_sets(connection: &Connection) -> Vec<CardSetSettings> {
let mut stmt = connection.prepare("SELECT id, name, forward, backward, filter FROM card_set").unwrap();
let iter = stmt.query_map([], |row| {
Ok(CardSetSettings {
id: row.get(0)?,
name: row.get(1)?,
forward: row.get(2)?,
backward: row.get(3)?,
filter: row.get(4)?,
count: None
})
}).unwrap();
let mut buffer = vec![];
for word in iter {
buffer.push(word.unwrap());
}
buffer
}
pub fn add_set(set: &mut CardSetSettings, connection: &Connection) {
let index = connection
.query_row(
"INSERT INTO card_set (name, forward, backward, filter) VALUES (?1, ?2, ?3, ?4) RETURNING id",
(
&set.name,
&set.forward,
&set.backward,
&set.filter,
),
|row| row.get(0)
)
.unwrap_or_else(|e| {println!("{}", e); 0});
set.id = index;
}
pub fn update_card_set(set: &mut CardSetSettings, connection: &Connection){
if set.id == 0 {
add_set(set, &connection);
}
else {
connection
.execute(
"UPDATE card_set SET name = ?1, forward = ?2, backward = ?3, filter = ?4 WHERE id = ?5",
(
&set.name,
&set.forward,
&set.backward,
&set.filter,
&set.id
),
)
.unwrap_or_else(|e| {println!("{}", e); 0});
}
}
pub fn delete_set(set: &CardSetSettings, connection: &Connection) {
if set.id == 0 {
return;
}
connection
.execute("DELETE FROM card_set WHERE id = ?1", (&set.id,))
.unwrap_or_else(|e| {
println!("{}", e);
0
});
}
+76
View File
@@ -0,0 +1,76 @@
use std::collections::HashMap;
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;
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 iter = stmt.query_map((set.id,), |row| {
Ok(CardStatistics {
id: row.get(0)?,
word_id: row.get(1)?,
score: row.get(2)?,
last_open: row.get(2)?,
})
}).unwrap();
let mut buffer = vec![];
for word in iter {
buffer.push(word.unwrap());
}
buffer
}
pub fn add_set(set: &mut CardSetSettings, connection: &Connection) {
let index = connection
.query_row(
"INSERT INTO card_set (name, forward, backward, filter) VALUES (?1, ?2, ?3, ?4) RETURNING id",
(
&set.name,
&set.forward,
&set.backward,
&set.filter,
),
|row| row.get(0)
)
.unwrap_or_else(|e| {println!("{}", e); 0});
set.id = index;
}
pub fn update_card_set(set: &mut CardSetSettings, connection: &Connection){
if set.id == 0 {
add_set(set, &connection);
}
else {
connection
.execute(
"UPDATE card_set SET name = ?1, forward = ?2, backward = ?3, filter = ?4 WHERE id = ?5",
(
&set.name,
&set.forward,
&set.backward,
&set.filter,
&set.id
),
)
.unwrap_or_else(|e| {println!("{}", e); 0});
}
}
pub fn delete_set(set: &CardSetSettings, connection: &Connection) {
if set.id == 0 {
return;
}
connection
.execute("DELETE FROM card_set WHERE id = ?1", (&set.id,))
.unwrap_or_else(|e| {
println!("{}", e);
0
});
}
+7
View File
@@ -0,0 +1,7 @@
pub(crate) mod words;
pub(crate) mod card_sets;
pub(crate) mod card_stats;
use crate::lang;
use crate::dictionary;
+150
View File
@@ -0,0 +1,150 @@
use crate::dictionary::app_data_dir;
use crate::lang::DictionaryElement;
use rusqlite::Connection;
use std::collections::HashMap;
pub fn create_db() {
let path = app_data_dir();
let db_file = path.join("data.db");
if !db_file.exists() {
std::fs::File::create(&db_file).unwrap();
let connection = Connection::open(&db_file).unwrap();
create_words_table(&connection);
create_card_stat_table(&connection);
create_card_set_table(&connection);
}
}
pub fn add_word(word: &mut DictionaryElement, connection: &Connection) {
let index = connection
.query_row(
"INSERT INTO words (key, value, tags, more) VALUES (?1, ?2, ?3, ?4) RETURNING id",
(
&word.key,
&word.value,
&word.tags,
serde_json::to_string(&word.additional).unwrap(),
),
|row| row.get(0),
)
.unwrap_or_else(|e| {
println!("{}", e);
0
});
word.id = index;
}
pub fn update_word(word: &mut DictionaryElement, connection: &Connection) {
if word.id == 0 {
add_word(word, &connection);
} else {
connection
.execute(
"UPDATE words SET key = ?1, value = ?2, tags = ?3, more = ?4 WHERE id = ?5",
(
&word.key,
&word.value,
&word.tags,
serde_json::to_string(&word.additional).unwrap(),
&word.id,
),
)
.unwrap_or_else(|e| {
println!("{}", e);
0
});
}
}
pub fn delete_word(word: &DictionaryElement, connection: &Connection) {
if word.id == 0 {
return;
}
connection
.execute("DELETE FROM words WHERE id = ?1", (&word.id,))
.unwrap_or_else(|e| {
println!("{}", e);
0
});
}
pub fn load_words(connection: &Connection) -> Vec<DictionaryElement> {
let mut stmt = connection
.prepare("SELECT id, key, value, tags, more FROM words")
.unwrap();
let word_iter = stmt
.query_map([], |row| {
let addinionals: String = row.get(4)?;
Ok(DictionaryElement {
id: row.get(0)?,
key: row.get(1)?,
value: row.get(2)?,
tags: row.get(3)?,
additional: serde_json::from_str::<HashMap<String, String>>(&addinionals).unwrap(),
})
})
.unwrap();
let mut buffer = vec![];
for word in word_iter {
buffer.push(word.unwrap());
}
buffer
}
fn create_words_table(conn: &Connection) {
conn.execute(
"CREATE TABLE words (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key TEXT NOT NULL,
value TEXT NOT NULL,
tags TEXT NOT NULL,
more TEXT
)",
(),
)
.unwrap_or_else(|e| {
println!("{}", e);
0
});
}
fn create_card_stat_table(conn: &Connection) {
conn.execute(
"CREATE TABLE card_stats (
id INTEGER PRIMARY KEY AUTOINCREMENT,
word_id INTEGER NOT NULL,
set_id TEXT NOT NULL,
score INTEGER NOT NULL DEFAULT 1,
last_opened INTEGER NOT NULL,
FOREIGN KEY (word_id) REFERENCES words (id) ON DELETE CASCADE,
FOREIGN KEY (set_id) REFERENCES sets (id) ON DELETE CASCADE
)",
(),
)
.unwrap_or_else(|e| {
println!("{}", e);
0
});
}
fn create_card_set_table(conn: &Connection) {
conn.execute(
"CREATE TABLE card_set (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
forward TEXT NOT NULL,
backward TEXT NOT NULL,
filter TEXT NOT NULL
)",
(),
)
.unwrap_or_else(|e| {
println!("{}", e);
0
});
}
pub struct Qwerty {}
+38 -28
View File
@@ -1,11 +1,12 @@
use crate::data_provider::words::{delete_word, update_word};
use crate::dictionary::DictionaryMessage::Test;
use crate::dictionary_test::DictionaryQuizState;
use crate::lang::DictionaryElement;
use crate::{AppState, NavigatedPage, Page, RootMessage};
use iced::alignment::Vertical::Center;
use iced::widget::button::Style;
use iced::widget::*;
use iced::{Border, Color, Length, Shadow, Task};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
@@ -22,31 +23,13 @@ pub struct DictionaryState {
no_typing: bool,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DictionaryElement {
pub key: String,
pub value: String,
pub tags: String,
pub additional: HashMap<String, String>,
}
impl DictionaryElement {
fn new() -> Self {
Self {
key: String::new(),
value: String::new(),
tags: String::new(),
additional: Default::default(),
}
}
}
#[derive(Debug, Clone)]
pub enum DictionaryMessage {
Back,
SetTags(usize, String),
SetKey(usize, String),
SetValue(usize, String),
SubmitWord(usize),
Remove(usize),
NewWord,
Include(usize, bool),
@@ -109,6 +92,7 @@ impl DictionaryState {
dict.push(DictionaryElement::new());
self.include_map.push(false);
}
DictionaryMessage::SetKey(i, v) => {
let dict = &mut self.state.lock().unwrap().dictionary;
dict[i].key = v
@@ -116,7 +100,7 @@ impl DictionaryState {
DictionaryMessage::SetValue(i, v) => {
let dict = &mut self.state.lock().unwrap().dictionary;
dict[i].value = v
},
}
DictionaryMessage::SetTags(i, v) => {
{
let dict = &mut self.state.lock().unwrap().dictionary;
@@ -125,32 +109,45 @@ impl DictionaryState {
self.update_tags();
}
DictionaryMessage::Remove(i) => {
let dict = &mut self.state.lock().unwrap().dictionary;
dict.remove(i);
let state = &mut self.state.lock().unwrap();
let dict = &mut state.dictionary;
let word = dict.remove(i);
delete_word(&word, &state.connection)
}
DictionaryMessage::Include(i, b) => self.include_map[i] = b,
DictionaryMessage::IncludeTag(t, v) => {
self.tag_map.insert(t, v);
self.update_words_include()
}
DictionaryMessage::ResetTags => {
self.tag_map.iter_mut().for_each(|(_, v)| *v = false);
self.include_map.iter_mut().for_each(|x| *x = false)
}
DictionaryMessage::Save => {
let dir = dict_file();
/* let dir = dict_file();
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));
.unwrap_or_else(|e| println!("Can't write file: {}", e));*/
}
DictionaryMessage::SetReverse(v) => self.reverse = v,
DictionaryMessage::Search(s) => {
self.search = s;
}
DictionaryMessage::SetTyping(b) => self.no_typing = b,
DictionaryMessage::SubmitWord(i) => {
let state = &mut self.state.lock().unwrap();
let connection = &state.connection;
let word = &mut state.dictionary.get(i).unwrap().clone();
update_word(word, &connection);
state.dictionary[i] = word.clone();
}
_ => {}
}
@@ -206,19 +203,22 @@ impl DictionaryState {
text_input("Ключ", &word.key)
.size(20)
.width(Length::Fill)
.on_input(move |string| DictionaryMessage::SetKey(i, string)),
.on_input(move |string| DictionaryMessage::SetKey(i, string))
.on_submit(DictionaryMessage::SubmitWord(i)),
);
line = line.push(
text_input("Значение", &word.value)
.size(20)
.width(Length::Fill)
.on_input(move |string| DictionaryMessage::SetValue(i, string)),
.on_input(move |string| DictionaryMessage::SetValue(i, string))
.on_submit(DictionaryMessage::SubmitWord(i)),
);
line = line.push(
text_input("Тэги", &word.tags)
.size(20)
.width(Length::Fill)
.on_input(move |string| DictionaryMessage::SetTags(i, string)),
.on_input(move |string| DictionaryMessage::SetTags(i, string))
.on_submit(DictionaryMessage::SubmitWord(i)),
);
line = line
@@ -364,3 +364,13 @@ pub fn dict_file() -> PathBuf {
dir.push("dict.json");
dir
}
pub fn app_data_dir() -> PathBuf {
let mut dir = dirs::data_dir().unwrap();
dir.push("jap_learn");
if !dir.exists() {
fs::create_dir(dir.clone()).unwrap();
}
dir
}
+3 -1
View File
@@ -1,4 +1,4 @@
use crate::dictionary::{split_with_coma, DictionaryElement};
use crate::dictionary::{split_with_coma};
use crate::quiz::Score;
use crate::Page::PreviousPage;
use crate::RootMessage;
@@ -9,6 +9,8 @@ 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::lang::DictionaryElement;
#[derive(Debug, Clone)]
pub struct DictionaryQuizState {
words: Vec<DictionaryElement>,
+102 -1
View File
@@ -1,3 +1,13 @@
use crate::repetitions::CardSetSettings;
use crate::AppState;
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use crate::data_provider::card_stats::load_stats_of_set;
const MAX_SCORE: u8 = 25_u8;
const FADE_PER_DAY: f32 = 0.95;
#[derive(Clone, Debug)]
pub struct KanaSet {
name: String,
@@ -177,7 +187,7 @@ impl KanaSet {
}
}
/* pub fn next(&mut self) -> (String, String) {
/* pub fn next(&mut self) -> (String, String) {
let current_set = self.list();
let mut rand = rand::rng();
@@ -210,3 +220,94 @@ impl PartialEq<Self> for KanaSet {
self.name == other.name
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DictionaryElement {
pub id: u32,
pub key: String,
pub value: String,
pub tags: String,
pub additional: HashMap<String, String>,
}
impl DictionaryElement {
pub fn new() -> Self {
Self {
id: 0,
key: String::new(),
value: String::new(),
tags: String::new(),
additional: Default::default(),
}
}
}
#[derive(Clone)]
pub struct CardStatistics {
pub id: u32,
pub word_id: u32,
pub last_open: DateTime<Utc>,
pub score: u8,
}
impl CardStatistics {
pub fn open(&mut self, status: WordOpenMode) {
self.last_open = Utc::now();
match status {
WordOpenMode::Easy => {
self.score += 5;
}
WordOpenMode::Ok => {
self.score += 3;
}
WordOpenMode::Hard => {
self.score -= 1;
}
WordOpenMode::None => {
self.score /= 2;
}
}
if self.score < 1 {
self.score = 1
} else if self.score > MAX_SCORE {
self.score = MAX_SCORE
}
}
pub fn calculated_score(&self) -> f32 {
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
}
}
pub enum WordOpenMode {
Easy,
Ok,
Hard,
None,
}
#[derive(Clone)]
pub struct CardSet {
set: Vec<CardStatistics>,
last_weights: Vec<f32>,
}
impl CardSet {
pub fn new(settings: CardSetSettings, state: Arc<Mutex<AppState>>) -> Self {
let state_locked = state.lock().unwrap();
let current_set = load_stats_of_set(&settings, &state_locked.connection);
let last_list = settings.get_word_list(&state_locked);
let weights = current_set.iter().map(|x| x.calculated_score()).collect();
Self {
set: current_set,
last_weights: weights,
}
}
}
+19 -17
View File
@@ -8,15 +8,16 @@ mod repetition;
mod repetitions;
mod selector;
mod writing;
use std::fs::File;
use std::io::Read;
use crate::dictionary::{DictionaryElement, DictionaryMessage, DictionaryState};
mod data_provider;
use crate::data_provider::card_sets::load_sets;
use crate::data_provider::words::{create_db, load_words};
use crate::dictionary::{app_data_dir, DictionaryMessage, DictionaryState};
use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState};
use crate::lang::DictionaryElement;
use crate::quiz::*;
use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState};
use crate::repetition::{RepetitionMessage, RepetitionState};
use crate::repetitions::{RepetitionsMessage, RepetitionsState};
use crate::repetitions::{CardSetSettings, RepetitionsMessage, RepetitionsState};
use crate::selector::*;
use crate::writing::{WritingMessage, WritingState};
use crate::Page::{
@@ -25,6 +26,7 @@ use crate::Page::{
use iced::widget::text;
use iced::Element;
use iced::{Font, Task};
use rusqlite::Connection;
use std::sync::{Arc, Mutex};
fn main() -> iced::Result {
@@ -32,7 +34,8 @@ fn main() -> iced::Result {
.title("Kana learn app")
.font(include_bytes!("../noto.ttf"))
.default_font(Font::with_name("Noto Sans JP"))
.run()
.
run()
}
#[derive(Debug, Clone)]
@@ -65,21 +68,19 @@ pub struct ScreenState {
pub struct AppState {
pub dictionary: Vec<DictionaryElement>,
pub card_sets: Vec<CardSetSettings>,
pub connection: Connection
}
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<DictionaryElement> = serde_json::from_str(&current_dict).unwrap();
let state = Arc::new(Mutex::new(AppState { dictionary: list }));
let path = app_data_dir();
let db_file = path.join("data.db");
let connection = Connection::open(db_file).unwrap();
let list: Vec<DictionaryElement> = load_words(&connection);
let sets: Vec<CardSetSettings> = load_sets(&connection);
let state = Arc::new(Mutex::new(AppState { dictionary: list, card_sets: sets, connection }));
ScreenState {
stack: vec![Selector(SelectorState::new(state.clone()))],
}
@@ -88,6 +89,7 @@ impl Default for ScreenState {
impl ScreenState {
pub fn boot() -> (ScreenState, Task<RootMessage>) {
create_db();
(ScreenState::default(), Task::none())
}
pub fn update(&mut self, message: RootMessage) -> Task<RootMessage> {
+10 -10
View File
@@ -1,16 +1,15 @@
use std::sync::{Arc, Mutex};
use crate::Page::PreviousPage;
use crate::{NavigatedPage, Page, RootMessage};
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;
#[derive(Clone, Debug)]
#[derive(Clone)]
pub struct RepetitionState {
}
impl Default for RepetitionState {
fn default() -> Self {
RepetitionState::new()
}
pub set: CardSetSettings,
pub state: Arc<Mutex<AppState>>,
}
impl NavigatedPage<RepetitionMessage> for RepetitionState {
@@ -24,9 +23,10 @@ impl NavigatedPage<RepetitionMessage> for RepetitionState {
}
impl RepetitionState {
pub(crate) fn new() -> RepetitionState {
pub(crate) fn new(set: CardSetSettings, state: Arc<Mutex<AppState>>) -> RepetitionState {
RepetitionState {
set,
state
}
}
}
+77 -64
View File
@@ -1,7 +1,9 @@
use crate::dictionary::DictionaryElement;
use crate::Page::PreviousPage;
use crate::data_provider::card_sets::{delete_set, update_card_set};
use crate::lang::DictionaryElement;
use crate::repetition::RepetitionState;
use crate::Page::{PreviousPage, Repetition};
use crate::{AppState, NavigatedPage, Page, RootMessage};
use iced::widget::button::{Catalog, Style};
pub 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};
@@ -10,7 +12,6 @@ use std::sync::{Arc, Mutex};
#[derive(Clone)]
pub struct RepetitionsState {
sets: Vec<CardSet>,
selected_set: Option<usize>,
correct_filters: Vec<bool>,
pub state: Arc<Mutex<AppState>>,
@@ -20,6 +21,10 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
fn navigate(&self, message: &RepetitionsMessage) -> Option<Page> {
if let RepetitionsMessage::Back = message {
Some(PreviousPage)
}
else if let RepetitionsMessage::GoToRepetition = message {
let clone = self.state.clone();
Some(Repetition(RepetitionState::new(self.state.lock().unwrap().card_sets[self.selected_set.unwrap()].clone(), clone) ))
} else {
None
}
@@ -28,10 +33,10 @@ impl NavigatedPage<RepetitionsMessage> for RepetitionsState {
impl RepetitionsState {
pub(crate) fn new(state: Arc<Mutex<AppState>>) -> RepetitionsState {
let count = state.lock().unwrap().card_sets.len();
RepetitionsState {
sets: Vec::new(),
selected_set: None,
correct_filters: vec![],
correct_filters: vec![true; count],
state,
}
}
@@ -39,52 +44,54 @@ impl RepetitionsState {
impl RepetitionsState {
pub fn update(&mut self, message: RepetitionsMessage) -> Task<RootMessage> {
let mut state = self.state.lock().unwrap();
match message {
RepetitionsMessage::Next => {}
RepetitionsMessage::Back => {}
RepetitionsMessage::GoToRepetition => {}
RepetitionsMessage::CreateSet => {
self.sets.push(CardSet::with_name(format!(
"Card set #{}",
self.sets.len() + 1
)));
let index = state.card_sets.len() + 1;
state
.card_sets
.push(CardSetSettings::with_name(format!("Card set #{}", index)));
self.correct_filters.push(true);
}
RepetitionsMessage::DeleteSet => {
self.sets.remove(self.selected_set.unwrap());
let set = state.card_sets.remove(self.selected_set.unwrap());
self.correct_filters.remove(self.selected_set.unwrap());
self.selected_set = None;
delete_set(&set, &state.connection);
}
RepetitionsMessage::SelectSet(index) => {
self.selected_set = Some(index);
}
RepetitionsMessage::SetName(new) => {
self.sets[self.selected_set.unwrap()].name = new;
state.card_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();
for i in 0..state.card_sets.len() {
self.correct_filters[i] = state.card_sets[i].check_filter();
}
if self.correct_filters.iter().all(|x| *x) {
println!("Saving");
} else {
println!("Some errors");
}
let word = &mut state.card_sets[self.selected_set.unwrap()].clone();
update_card_set(word, &state.connection);
state.card_sets[self.selected_set.unwrap()] = word.clone();
}
RepetitionsMessage::SetForward(new) => {
self.sets[self.selected_set.unwrap()].forward = new;
state.card_sets[self.selected_set.unwrap()].forward = new;
}
RepetitionsMessage::SetBackward(new) => {
self.sets[self.selected_set.unwrap()].backward = new;
state.card_sets[self.selected_set.unwrap()].backward = new;
}
RepetitionsMessage::SetFilter(new) => {
self.sets[self.selected_set.unwrap()].filter = new;
state.card_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);
let set = state.card_sets.get(self.selected_set.unwrap()).unwrap();
let count = set.get_word_list(&state).len();
state.card_sets[self.selected_set.unwrap()].count = Some(count);
}
}
Task::none()
@@ -100,9 +107,6 @@ impl RepetitionsState {
button("Добавить")
.width(Fill)
.on_press(RepetitionsMessage::CreateSet),
button("Сохранить")
.width(Fill)
.on_press(RepetitionsMessage::Save),
]
.spacing(10)
.width(Length::FillPortion(1)),
@@ -115,12 +119,12 @@ impl RepetitionsState {
.width(Fill)
.height(Fill)
]
.align_x(Left)
.width(Fill),
.align_x(Left)
.width(Fill),
)
.center_x(Fill)
.padding(10)
.into()
.center_x(Fill)
.padding(10)
.into()
}
fn launch_button(&self) -> Element<'_, RepetitionsMessage> {
@@ -135,43 +139,48 @@ impl RepetitionsState {
fn selected_set_view(&self) -> Element<'_, RepetitionsMessage> {
if let Some(index) = self.selected_set {
let sets = &self.state.lock().unwrap().card_sets;
return column![
scrollable(
column![
text_input("Название набора", &self.sets[index].name)
text_input("Название набора", &sets[index].name)
.on_input(RepetitionsMessage::SetName),
text_input("Передняя сторона", &self.sets[index].forward)
text_input("Передняя сторона", &sets[index].forward)
.on_input(RepetitionsMessage::SetForward),
text_input("Задняя сторона", &self.sets[index].backward)
text_input("Задняя сторона", &sets[index].backward)
.on_input(RepetitionsMessage::SetBackward),
text!("Фильтр"),
text_input("", &self.sets[index].filter)
.on_input(RepetitionsMessage::SetFilter),
text_input("", &sets[index].filter).on_input(RepetitionsMessage::SetFilter),
button("Проверить фильтр").on_press(RepetitionsMessage::TryFilter),
self.count_view()
self.count_view(&sets[index])
]
.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),
row![
button("Сохранить").on_press(RepetitionsMessage::Save),
button("Удалить")
.style(|x: &Theme, _status| Style {
background: Some(Color(x.palette().danger)),
text_color: x.palette().text,
border: Border::default().rounded(2),
shadow: Default::default(),
snap: false,
})
.on_press(RepetitionsMessage::DeleteSet)
]
.spacing(10),
]
.spacing(10)
.width(Length::FillPortion(2))
.into();
.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 {
fn count_view(&self, set: &CardSetSettings) -> Element<'_, RepetitionsMessage> {
if let Some(count) = set.count {
return text!("Колличество слов: {}", count).into();
}
space().into()
@@ -180,7 +189,8 @@ impl RepetitionsState {
fn sets_list(&self) -> Column<'_, RepetitionsMessage> {
let mut column = Column::new();
let mut i = 0;
for set in &self.sets {
let sets = &self.state.lock().unwrap().card_sets;
for set in sets {
column = column.push(
button(text!("{}", set.name.clone()))
.on_press_with(move || RepetitionsMessage::SelectSet(i.clone()))
@@ -219,17 +229,19 @@ pub enum RepetitionsMessage {
}
#[derive(Debug, Clone)]
pub struct CardSet {
name: String,
forward: String,
backward: String,
filter: String,
count: Option<usize>,
pub struct CardSetSettings {
pub id: u32,
pub name: String,
pub forward: String,
pub backward: String,
pub filter: String,
pub count: Option<usize>,
}
impl CardSet {
fn with_name(name: String) -> CardSet {
CardSet {
impl CardSetSettings {
fn with_name(name: String) -> CardSetSettings {
CardSetSettings {
id: 0,
name,
forward: "".to_string(),
backward: "".to_string(),
@@ -260,7 +272,8 @@ impl CardSet {
more.insert(iced.0.clone().into(), iced.1.clone().into());
}
let mut scope = Scope::new();
scope.push_constant("key", word.key.clone())
scope
.push_constant("key", word.key.clone())
.push_constant("value", word.value.clone())
.push_constant("tags", word.tags.clone())
.push_constant("more", more);