Add word group to database and model

This commit is contained in:
2026-05-05 12:02:59 +03:00
parent bc0e360b96
commit 9cb034066e
8 changed files with 193 additions and 132 deletions
+76 -59
View File
@@ -1,5 +1,5 @@
use crate::dictionary::app_data_dir; use crate::dictionary::app_data_dir;
use crate::lang::DictionaryElement; use crate::lang::{WordData, WordGroup};
use rusqlite::Connection; use rusqlite::Connection;
use std::collections::HashMap; use std::collections::HashMap;
@@ -10,13 +10,61 @@ pub fn create_db() {
std::fs::File::create(&db_file).unwrap(); std::fs::File::create(&db_file).unwrap();
let connection = Connection::open(&db_file).unwrap(); let connection = Connection::open(&db_file).unwrap();
connection.execute("PRAGMA foreign_keys = ON;", []).unwrap(); connection.execute("PRAGMA foreign_keys = ON;", []).unwrap();
create_words_table(&connection);
create_card_stat_table(&connection); create_tables(&connection);
create_card_set_table(&connection);
} }
} }
pub fn add_word(word: &mut DictionaryElement, connection: &Connection) { fn create_tables(conn: &Connection) {
conn.execute(
"
CREATE TABLE word_group (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
);
insert into word_group (name)
values (\"Слова\");
CREATE TABLE words (
id INTEGER PRIMARY KEY AUTOINCREMENT,
key TEXT NOT NULL,
value TEXT NOT NULL,
tags TEXT NOT NULL,
more TEXT,
group_id INTEGER NOT NULL DEFAULT 1,
FOREIGN KEY(group_id) REFERENCES word_group(id)
);
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 card_set (id) ON DELETE CASCADE
);
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 fn add_word(word: &mut WordData, connection: &Connection) {
let index = connection let index = connection
.query_row( .query_row(
"INSERT INTO words (key, value, tags, more) VALUES (?1, ?2, ?3, ?4) RETURNING id", "INSERT INTO words (key, value, tags, more) VALUES (?1, ?2, ?3, ?4) RETURNING id",
@@ -36,7 +84,7 @@ pub fn add_word(word: &mut DictionaryElement, connection: &Connection) {
word.id = index; word.id = index;
} }
pub fn update_word(word: &mut DictionaryElement, connection: &Connection) { pub fn update_word(word: &mut WordData, connection: &Connection) {
if word.id == 0 { if word.id == 0 {
add_word(word, &connection); add_word(word, &connection);
} else { } else {
@@ -58,7 +106,7 @@ pub fn update_word(word: &mut DictionaryElement, connection: &Connection) {
} }
} }
pub fn delete_word(word: &DictionaryElement, connection: &Connection) { pub fn delete_word(word: &WordData, connection: &Connection) {
if word.id == 0 { if word.id == 0 {
return; return;
} }
@@ -70,19 +118,20 @@ pub fn delete_word(word: &DictionaryElement, connection: &Connection) {
}); });
} }
pub fn load_words(connection: &Connection) -> Vec<DictionaryElement> { pub fn load_words(connection: &Connection) -> Vec<WordData> {
let mut stmt = connection let mut stmt = connection
.prepare("SELECT id, key, value, tags, more FROM words") .prepare("SELECT id, key, value, tags, more, group_id FROM words")
.unwrap(); .unwrap();
let word_iter = stmt let word_iter = stmt
.query_map([], |row| { .query_map([], |row| {
let addinionals: String = row.get(4)?; let addinionals: String = row.get(4)?;
Ok(DictionaryElement { Ok(WordData {
id: row.get(0)?, id: row.get(0)?,
key: row.get(1)?, key: row.get(1)?,
value: row.get(2)?, value: row.get(2)?,
tags: row.get(3)?, tags: row.get(3)?,
additional: serde_json::from_str::<HashMap<String, String>>(&addinionals).unwrap(), additional: serde_json::from_str::<HashMap<String, String>>(&addinionals).unwrap(),
group_id: row.get(5)?,
}) })
}) })
.unwrap(); .unwrap();
@@ -95,55 +144,23 @@ pub fn load_words(connection: &Connection) -> Vec<DictionaryElement> {
buffer buffer
} }
fn create_words_table(conn: &Connection) { pub fn load_word_groups(connection: &Connection) -> Vec<WordGroup> {
conn.execute( let mut stmt = connection
"CREATE TABLE words ( .prepare("SELECT id, name FROM word_group")
id INTEGER PRIMARY KEY AUTOINCREMENT, .unwrap();
key TEXT NOT NULL, let group_iter = stmt
value TEXT NOT NULL, .query_map([], |row| {
tags TEXT NOT NULL, Ok(WordGroup {
more TEXT id: row.get(0)?,
)", name: row.get(1)?,
(), })
) })
.unwrap_or_else(|e| { .unwrap();
println!("{}", e);
0 let mut buffer = vec![];
}); for group in group_iter {
buffer.push(group.unwrap());
} }
fn create_card_stat_table(conn: &Connection) { buffer
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 card_set (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
});
} }
+4 -3
View File
@@ -1,7 +1,7 @@
use crate::data_provider::words::{delete_word, update_word}; use crate::data_provider::words::{delete_word, update_word};
use crate::dictionary::DictionaryMessage::Test; use crate::dictionary::DictionaryMessage::Test;
use crate::dictionary_test::DictionaryQuizState; use crate::dictionary_test::DictionaryQuizState;
use crate::lang::DictionaryElement; use crate::lang::WordData;
use crate::word::WordState; use crate::word::WordState;
use crate::Page::Word; use crate::Page::Word;
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING}; use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
@@ -41,6 +41,7 @@ pub enum DictionaryMessage {
SetReverse(bool), SetReverse(bool),
Search(String), Search(String),
SetTyping(bool), SetTyping(bool),
} }
impl NavigatedPage<DictionaryMessage> for DictionaryState { impl NavigatedPage<DictionaryMessage> for DictionaryState {
@@ -66,7 +67,7 @@ impl NavigatedPage<DictionaryMessage> for DictionaryState {
} }
} }
if let DictionaryMessage::WordAction(index) = message { if let DictionaryMessage::WordAction(index) = message {
let word : DictionaryElement; let word : WordData;
{ {
let state = self.state.lock().unwrap(); let state = self.state.lock().unwrap();
let dict = &state.dictionary; let dict = &state.dictionary;
@@ -101,7 +102,7 @@ impl DictionaryState {
match message { match message {
DictionaryMessage::NewWord => { DictionaryMessage::NewWord => {
let dict = &mut self.state.lock().unwrap().dictionary; let dict = &mut self.state.lock().unwrap().dictionary;
dict.push(DictionaryElement::new()); dict.push(WordData::new());
self.include_map.push(false); self.include_map.push(false);
} }
+4 -4
View File
@@ -9,12 +9,12 @@ use iced::widget::{button, container, row, space, text, text_input, Row};
use iced::Background::Color; use iced::Background::Color;
use iced::{alignment, Border, Element, Fill, Task, Theme}; use iced::{alignment, Border, Element, Fill, Task, Theme};
use rand::prelude::SliceRandom; use rand::prelude::SliceRandom;
use crate::lang::DictionaryElement; use crate::lang::WordData;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct DictionaryQuizState { pub struct DictionaryQuizState {
words: Vec<DictionaryElement>, words: Vec<WordData>,
current_set: Vec<DictionaryElement>, current_set: Vec<WordData>,
answer: String, answer: String,
view: String, view: String,
correct: String, correct: String,
@@ -43,7 +43,7 @@ impl NavigatedPage<DictionaryQuizMessage> for DictionaryQuizState {
impl DictionaryQuizState { impl DictionaryQuizState {
pub fn new( pub fn new(
words: Vec<DictionaryElement>, words: Vec<WordData>,
reverse: bool, reverse: bool,
no_typing: bool, no_typing: bool,
) -> DictionaryQuizState { ) -> DictionaryQuizState {
+12 -5
View File
@@ -229,15 +229,16 @@ impl PartialEq<Self> for KanaSet {
} }
#[derive(Clone, Debug, Serialize, Deserialize)] #[derive(Clone, Debug, Serialize, Deserialize)]
pub struct DictionaryElement { pub struct WordData {
pub id: u32, pub id: u32,
pub key: String, pub key: String,
pub value: String, pub value: String,
pub tags: String, pub tags: String,
pub additional: HashMap<String, String>, pub additional: HashMap<String, String>,
pub group_id: u32
} }
impl DictionaryElement { impl WordData {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
id: 0, id: 0,
@@ -245,10 +246,16 @@ impl DictionaryElement {
value: String::new(), value: String::new(),
tags: String::new(), tags: String::new(),
additional: Default::default(), additional: Default::default(),
group_id: 1
} }
} }
} }
pub struct WordGroup{
pub id: u32,
pub name: String,
}
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq)]
pub struct CardStatistics { pub struct CardStatistics {
pub id: u32, pub id: u32,
@@ -302,7 +309,7 @@ pub enum WordOpenMode {
#[derive(Clone)] #[derive(Clone)]
pub struct CardSet { pub struct CardSet {
words: Vec<DictionaryElement>, words: Vec<WordData>,
set: Vec<CardStatistics>, set: Vec<CardStatistics>,
last_weights: WeightedIndex<f32>, last_weights: WeightedIndex<f32>,
current_word_index: Option<usize>, current_word_index: Option<usize>,
@@ -360,7 +367,7 @@ impl CardSet {
} }
pub fn next(&mut self) -> DictionaryElement { pub fn next(&mut self) -> (WordData, CardStatistics) {
let index = self.last_weights.sample(&mut self.generator); let index = self.last_weights.sample(&mut self.generator);
if self.history.contains(&index) { if self.history.contains(&index) {
@@ -372,7 +379,7 @@ impl CardSet {
} }
self.history.push(index); self.history.push(index);
self.current_word_index = Some(index); self.current_word_index = Some(index);
self.words[index].clone() (self.words[index].clone(), self.set[index].clone())
} }
pub fn open(&mut self, status: WordOpenMode) { pub fn open(&mut self, status: WordOpenMode) {
+21 -16
View File
@@ -1,4 +1,5 @@
#![windows_subsystem = "windows"] #![windows_subsystem = "windows"]
mod data_provider;
mod dictionary; mod dictionary;
mod dictionary_test; mod dictionary_test;
mod lang; mod lang;
@@ -7,15 +8,14 @@ mod randomizer;
mod repetition; mod repetition;
mod repetitions; mod repetitions;
mod selector; mod selector;
mod writing;
mod data_provider;
mod word; mod word;
mod writing;
use crate::data_provider::card_sets::load_sets; use crate::data_provider::card_sets::load_sets;
use crate::data_provider::words::{create_db, load_words}; use crate::data_provider::words::{create_db, load_word_groups, load_words};
use crate::dictionary::{app_data_dir, DictionaryMessage, DictionaryState}; use crate::dictionary::{app_data_dir, DictionaryMessage, DictionaryState};
use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState}; use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState};
use crate::lang::DictionaryElement; use crate::lang::{WordData, WordGroup};
use crate::quiz::*; use crate::quiz::*;
use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState}; use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState};
use crate::repetition::{RepetitionMessage, RepetitionState}; use crate::repetition::{RepetitionMessage, RepetitionState};
@@ -24,7 +24,7 @@ use crate::selector::*;
use crate::word::{WordMessage, WordState}; use crate::word::{WordMessage, WordState};
use crate::writing::{WritingMessage, WritingState}; use crate::writing::{WritingMessage, WritingState};
use crate::Page::{ use crate::Page::{
Dictionary, DictionaryQuiz, Quiz, Randomizer, Repetition, Repetitions, Selector, Word, Writing Dictionary, DictionaryQuiz, Quiz, Randomizer, Repetition, Repetitions, Selector, Word, Writing,
}; };
use crate::RootMessage::Keyboard; use crate::RootMessage::Keyboard;
use iced::keyboard::Event; use iced::keyboard::Event;
@@ -37,12 +37,12 @@ use std::sync::{Arc, Mutex};
const DEFAULT_SPACING: f32 = 10.0; const DEFAULT_SPACING: f32 = 10.0;
fn main() -> iced::Result { fn main() -> iced::Result {
iced::application(ScreenState::boot, ScreenState::update, ScreenState::view).subscription(subscription) 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"))
. .run()
run()
} }
fn subscription(_state: &ScreenState) -> Subscription<RootMessage> { fn subscription(_state: &ScreenState) -> Subscription<RootMessage> {
@@ -81,9 +81,10 @@ pub struct ScreenState {
} }
pub struct AppState { pub struct AppState {
pub dictionary: Vec<DictionaryElement>, pub dictionary: Vec<WordData>,
pub card_sets: Vec<CardSetSettings>, pub card_sets: Vec<CardSetSettings>,
pub connection: Connection pub word_groups: Vec<WordGroup>,
pub connection: Connection,
} }
impl Default for ScreenState { impl Default for ScreenState {
@@ -92,10 +93,16 @@ impl Default for ScreenState {
let db_file = path.join("data.db"); let db_file = path.join("data.db");
let connection = Connection::open(db_file).unwrap(); let connection = Connection::open(db_file).unwrap();
connection.execute("PRAGMA foreign_keys = ON;", []).unwrap(); connection.execute("PRAGMA foreign_keys = ON;", []).unwrap();
let list: Vec<DictionaryElement> = load_words(&connection); let list = load_words(&connection);
let sets: Vec<CardSetSettings> = load_sets(&connection); let sets = load_sets(&connection);
let groups = load_word_groups(&connection);
let state = Arc::new(Mutex::new(AppState { dictionary: list, card_sets: sets, connection })); let state = Arc::new(Mutex::new(AppState {
dictionary: list,
card_sets: sets,
connection,
word_groups: groups,
}));
ScreenState { ScreenState {
stack: vec![Selector(SelectorState::new(state.clone()))], stack: vec![Selector(SelectorState::new(state.clone()))],
} }
@@ -147,11 +154,9 @@ impl ScreenState {
return match page { return match page {
Repetition(page) => page.press(&message), Repetition(page) => page.press(&message),
_ => Task::none(), _ => Task::none(),
};
} }
} }
}
#[macro_export] #[macro_export]
macro_rules! view_navigation { macro_rules! view_navigation {
+60 -29
View File
@@ -1,14 +1,14 @@
use std::collections::HashSet;
use crate::data_provider::voice::get_voice; use crate::data_provider::voice::get_voice;
use crate::lang::{CardSet, DictionaryElement, WordOpenMode}; use crate::lang::{CardSet, CardStatistics, WordData, WordOpenMode};
use crate::repetitions::CardSetSettings; use crate::repetitions::CardSetSettings;
use crate::Page::PreviousPage; use crate::Page::PreviousPage;
use crate::{AppState, KeyPressedPage, NavigatedPage, Page, RootMessage, DEFAULT_SPACING}; use crate::{AppState, KeyPressedPage, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use iced::alignment::Horizontal::Center; use iced::alignment::Horizontal::Center;
use iced::keyboard::key::Physical::Code; use iced::keyboard::key::Physical::Code;
use iced::widget::{button, column, container, row, rule, space, text}; use iced::widget::{button, column, container, row, rule, space, text, Column};
use iced::{alignment, keyboard, Element, Fill, Left, Task}; use iced::{alignment, keyboard, Element, Fill, Left, Task};
use rodio::MixerDeviceSink; use rodio::MixerDeviceSink;
use std::collections::HashSet;
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
use tokio::task::spawn_blocking; use tokio::task::spawn_blocking;
@@ -16,7 +16,8 @@ pub struct RepetitionState {
pub settings: CardSetSettings, pub settings: CardSetSettings,
pub set: CardSet, pub set: CardSet,
pub state: Arc<Mutex<AppState>>, pub state: Arc<Mutex<AppState>>,
current_word: DictionaryElement, current_word: WordData,
current_statistic: CardStatistics,
open: bool, open: bool,
can_play: bool, can_play: bool,
sink: Arc<MixerDeviceSink>, sink: Arc<MixerDeviceSink>,
@@ -36,7 +37,7 @@ 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 mut card_set = CardSet::new(&set, state.clone());
let word = card_set.next(); let (word, stat) = card_set.next();
let sink_handle = rodio::DeviceSinkBuilder::open_default_sink().unwrap(); let sink_handle = rodio::DeviceSinkBuilder::open_default_sink().unwrap();
RepetitionState { RepetitionState {
@@ -44,6 +45,7 @@ impl RepetitionState {
set: card_set, set: card_set,
state, state,
current_word: word, current_word: word,
current_statistic: stat,
open: false, open: false,
can_play: true, can_play: true,
sink: Arc::new(sink_handle), sink: Arc::new(sink_handle),
@@ -60,15 +62,17 @@ impl RepetitionState {
RepetitionMessage::Answer(m) => return self.answer(m), RepetitionMessage::Answer(m) => return self.answer(m),
RepetitionMessage::Play => { RepetitionMessage::Play => {
if !self.can_play { if !self.can_play {
return Task::none() return Task::none();
} }
self.can_play = false; self.can_play = false;
let value = self.current_word.key.clone(); let value = self.current_word.key.clone();
if self.settings.require_speech() { if self.settings.require_speech() {
return Task::perform(play_sound(self.sink.clone(), value), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished)); return Task::perform(play_sound(self.sink.clone(), value), |_| {
RootMessage::Repetition(RepetitionMessage::PlayFinished)
});
}
} }
},
RepetitionMessage::PlayFinished => { RepetitionMessage::PlayFinished => {
self.can_play = true; self.can_play = true;
} }
@@ -77,8 +81,6 @@ impl RepetitionState {
Task::none() Task::none()
} }
fn next(&mut self) -> Task<RootMessage> { fn next(&mut self) -> Task<RootMessage> {
if self.open { if self.open {
self.answer(WordOpenMode::None) self.answer(WordOpenMode::None)
@@ -96,10 +98,15 @@ impl RepetitionState {
self.set.open(mode); self.set.open(mode);
self.open = false; self.open = false;
self.opened.insert(self.current_word.id); self.opened.insert(self.current_word.id);
self.current_word = self.set.next(); let next = self.set.next();
self.current_word = next.0;
self.current_statistic = next.1;
if self.settings.require_speech() { if self.settings.require_speech() {
return Task::perform(play_sound(self.sink.clone(), self.current_word.key.clone()), |_| RootMessage::Repetition(RepetitionMessage::PlayFinished)); return Task::perform(
play_sound(self.sink.clone(), self.current_word.key.clone()),
|_| RootMessage::Repetition(RepetitionMessage::PlayFinished),
);
} }
Task::none() Task::none()
} }
@@ -123,8 +130,13 @@ impl RepetitionState {
container(self.answer_bar()) container(self.answer_bar())
.width(Fill) .width(Fill)
.align_x(Center) .align_x(Center)
.height(30), .height(60),
text!("Затронуто слов {}, {}%", self.opened.len(), (self.opened.len() as f32 / self.set.len() as f32 * 10000.0).round() / 100.0) text!(
"Затронуто слов {}, {}%",
self.opened.len(),
(self.opened.len() as f32 / self.set.len() as f32 * 10000.0).round()
/ 100.0
)
] ]
.height(Fill) .height(Fill)
.width(Fill) .width(Fill)
@@ -138,13 +150,8 @@ impl RepetitionState {
} }
fn draw_forward(&self) -> Element<'_, RepetitionMessage> { fn draw_forward(&self) -> Element<'_, RepetitionMessage> {
let word = &self.current_word; self.draw_card_view(self.settings.forward.as_str())
match self.settings.forward.as_str() {
"key" => self.draw_key(word),
"value" => self.draw_value(word),
"speech" => self.draw_voice(),
_ => space().into(),
}
} }
fn draw_backward(&self) -> Element<'_, RepetitionMessage> { fn draw_backward(&self) -> Element<'_, RepetitionMessage> {
@@ -152,13 +159,26 @@ impl RepetitionState {
return space().into(); return space().into();
} }
self.draw_card_view(self.settings.backward.as_str())
}
fn draw_card_view(&self, properties: &str) -> Element<'_, RepetitionMessage> {
let word = &self.current_word; let word = &self.current_word;
match self.settings.backward.as_str() {
let mut col = Column::new();
for view_type in properties.split(" ") {
col = col.push( match view_type {
"key" => self.draw_key(word), "key" => self.draw_key(word),
"value" => self.draw_value(word), "value" => self.draw_value(word),
"speech" => self.draw_voice(), "speech" => self.draw_voice(),
"reading" => self.draw_reading(word),
_ => space().into(), _ => space().into(),
})
} }
col.spacing(DEFAULT_SPACING).into()
} }
fn answer_bar(&self) -> Element<'_, RepetitionMessage> { fn answer_bar(&self) -> Element<'_, RepetitionMessage> {
@@ -166,6 +186,8 @@ impl RepetitionState {
return space().into(); return space().into();
} }
column![
text!("{} очков", self.current_statistic.calculated_score().round() as i32),
row![ row![
button("Не получилось").on_press(RepetitionMessage::Answer(WordOpenMode::None)), button("Не получилось").on_press(RepetitionMessage::Answer(WordOpenMode::None)),
button("Трудно").on_press(RepetitionMessage::Answer(WordOpenMode::Hard)), button("Трудно").on_press(RepetitionMessage::Answer(WordOpenMode::Hard)),
@@ -173,22 +195,31 @@ impl RepetitionState {
button("Легко").on_press(RepetitionMessage::Answer(WordOpenMode::Easy)), button("Легко").on_press(RepetitionMessage::Answer(WordOpenMode::Easy)),
] ]
.spacing(DEFAULT_SPACING) .spacing(DEFAULT_SPACING)
]
.align_x(Center)
.spacing(DEFAULT_SPACING)
.into() .into()
} }
fn draw_key(&self, word: &DictionaryElement) -> Element<'_, RepetitionMessage> { fn draw_key(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
text!("{}", word.key).size(36).into() text!("{}", word.key).size(36).into()
} }
fn draw_value(&self, word: &DictionaryElement) -> Element<'_, RepetitionMessage> { fn draw_value(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
text!("{}", word.value).size(24).into() text!("{}", word.value).size(24).into()
} }
fn draw_voice(&self) -> Element<'_, RepetitionMessage> { fn draw_voice(&self) -> Element<'_, RepetitionMessage> {
button("Воспроизвести") button("Воспроизвести")
.on_press(RepetitionMessage::Play) .on_press(RepetitionMessage::Play)
.into() .into()
} }
fn draw_reading(&self, word: &WordData) -> Element<'_, RepetitionMessage> {
match word.additional.get("reading") {
None => space().into(),
Some(reading) => text!("{}", reading).size(24).into(),
}
}
} }
impl KeyPressedPage for RepetitionState { impl KeyPressedPage for RepetitionState {
@@ -211,12 +242,10 @@ impl KeyPressedPage for RepetitionState {
keyboard::key::Code::Digit3 => self.answer(WordOpenMode::Ok), keyboard::key::Code::Digit3 => self.answer(WordOpenMode::Ok),
keyboard::key::Code::Digit4 => self.answer(WordOpenMode::Easy), keyboard::key::Code::Digit4 => self.answer(WordOpenMode::Easy),
_ => Task::none(), _ => Task::none(),
};
} }
} }
}
Task::none() Task::none()
} }
} }
@@ -233,5 +262,7 @@ async fn play_sound(sink: Arc<MixerDeviceSink>, text: String) {
let data = get_voice(text.as_str()).await; let data = get_voice(text.as_str()).await;
spawn_blocking(move || { spawn_blocking(move || {
rodio::play(&sink.mixer(), data).unwrap().sleep_until_end(); rodio::play(&sink.mixer(), data).unwrap().sleep_until_end();
}).await.unwrap(); })
.await
.unwrap();
} }
+2 -2
View File
@@ -1,5 +1,5 @@
use crate::data_provider::card_sets::{delete_set, update_card_set}; use crate::data_provider::card_sets::{delete_set, update_card_set};
use crate::lang::DictionaryElement; use crate::lang::WordData;
use crate::repetition::RepetitionState; use crate::repetition::RepetitionState;
use crate::Page::{PreviousPage, Repetition}; use crate::Page::{PreviousPage, Repetition};
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING}; use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
@@ -255,7 +255,7 @@ impl CardSetSettings {
ast.is_ok() ast.is_ok()
} }
pub fn get_word_list(&self, state: &AppState) -> Vec<DictionaryElement> { pub fn get_word_list(&self, state: &AppState) -> Vec<WordData> {
let mut list = vec![]; let mut list = vec![];
let engine = Engine::new(); let engine = Engine::new();
let ast = engine.compile(&self.filter); let ast = engine.compile(&self.filter);
+3 -3
View File
@@ -1,5 +1,5 @@
use crate::data_provider::words::{delete_word, update_word}; use crate::data_provider::words::{delete_word, update_word};
use crate::lang::DictionaryElement; use crate::lang::WordData;
use crate::Page::PreviousPage; use crate::Page::PreviousPage;
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING}; use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use iced::widget::button::danger; use iced::widget::button::danger;
@@ -11,7 +11,7 @@ use std::sync::{Arc, Mutex};
pub struct WordState { pub struct WordState {
state: Arc<Mutex<AppState>>, state: Arc<Mutex<AppState>>,
index: usize, index: usize,
word: DictionaryElement, word: WordData,
} }
impl NavigatedPage<WordMessage> for WordState { impl NavigatedPage<WordMessage> for WordState {
@@ -26,7 +26,7 @@ impl NavigatedPage<WordMessage> for WordState {
impl WordState { impl WordState {
pub(crate) fn new( pub(crate) fn new(
word: DictionaryElement, word: WordData,
index: usize, index: usize,
state: Arc<Mutex<AppState>>, state: Arc<Mutex<AppState>>,
) -> WordState { ) -> WordState {