Before button global styling
This commit is contained in:
Generated
+16
-1
@@ -765,7 +765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737"
|
checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.10.0",
|
"bitflags 2.10.0",
|
||||||
"fontdb",
|
"fontdb 0.23.0",
|
||||||
"harfrust",
|
"harfrust",
|
||||||
"linebender_resource_handle",
|
"linebender_resource_handle",
|
||||||
"log",
|
"log",
|
||||||
@@ -1200,6 +1200,19 @@ dependencies = [
|
|||||||
"ttf-parser",
|
"ttf-parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fontdb"
|
||||||
|
version = "0.24.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2660c5e9157bf76d2db1294e4a9feba604ef610819a3b591088d0d8392a3290f"
|
||||||
|
dependencies = [
|
||||||
|
"fontconfig-parser",
|
||||||
|
"log",
|
||||||
|
"memmap2",
|
||||||
|
"slotmap",
|
||||||
|
"tinyvec",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "foreign-types"
|
name = "foreign-types"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
@@ -2097,8 +2110,10 @@ dependencies = [
|
|||||||
"chrono",
|
"chrono",
|
||||||
"criterion",
|
"criterion",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
"fontdb 0.24.0",
|
||||||
"hex",
|
"hex",
|
||||||
"iced",
|
"iced",
|
||||||
|
"iced_core",
|
||||||
"rand",
|
"rand",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rhai",
|
"rhai",
|
||||||
|
|||||||
+2
-1
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { version = "0.14.0", features = ["tokio"]}
|
iced = { version = "0.14.0", features = ["tokio"]}
|
||||||
|
iced_core = "0.14.0"
|
||||||
rand = "0.10.1"
|
rand = "0.10.1"
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
@@ -18,7 +19,7 @@ sha2 = "0.11.0"
|
|||||||
tokio = { version = "1.50.0", features = ["rt", "rt-multi-thread", "macros"] }
|
tokio = { version = "1.50.0", features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
hex = "0.4.3"
|
hex = "0.4.3"
|
||||||
zstd = "0.13.3"
|
zstd = "0.13.3"
|
||||||
|
fontdb = "0.24.0"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
criterion = "0.8.2"
|
criterion = "0.8.2"
|
||||||
|
|
||||||
|
|||||||
+7
-5
@@ -5,8 +5,7 @@ use crate::dictionary::DictionaryMessage::{
|
|||||||
use crate::dictionary_test::DictionaryQuizState;
|
use crate::dictionary_test::DictionaryQuizState;
|
||||||
use crate::lang::{WordData, WordGroup};
|
use crate::lang::{WordData, WordGroup};
|
||||||
use crate::word::WordState;
|
use crate::word::WordState;
|
||||||
use crate::Page::Word;
|
use crate::{AppState, RootMessage };
|
||||||
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
|
|
||||||
use chrono::{DateTime, TimeDelta, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
use iced::alignment::Vertical::Center;
|
use iced::alignment::Vertical::Center;
|
||||||
use iced::widget::button::Style;
|
use iced::widget::button::Style;
|
||||||
@@ -22,6 +21,9 @@ use std::path::PathBuf;
|
|||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use DictionaryMessage::Back;
|
use DictionaryMessage::Back;
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::Word;
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct DictionaryState {
|
pub struct DictionaryState {
|
||||||
@@ -347,21 +349,21 @@ impl DictionaryState {
|
|||||||
|
|
||||||
line = line.push(
|
line = line.push(
|
||||||
text_input("Слово", &word.key)
|
text_input("Слово", &word.key)
|
||||||
.size(20)
|
.size(ACCENT_FONT_SIZE)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.on_input(move |string| DictionaryMessage::SetKey(i, string))
|
.on_input(move |string| DictionaryMessage::SetKey(i, string))
|
||||||
.on_submit(DictionaryMessage::SubmitWord(i)),
|
.on_submit(DictionaryMessage::SubmitWord(i)),
|
||||||
);
|
);
|
||||||
line = line.push(
|
line = line.push(
|
||||||
text_input("Перевод", &word.value)
|
text_input("Перевод", &word.value)
|
||||||
.size(20)
|
.size(ACCENT_FONT_SIZE)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.on_input(move |string| DictionaryMessage::SetValue(i, string))
|
.on_input(move |string| DictionaryMessage::SetValue(i, string))
|
||||||
.on_submit(DictionaryMessage::SubmitWord(i)),
|
.on_submit(DictionaryMessage::SubmitWord(i)),
|
||||||
);
|
);
|
||||||
line = line.push(
|
line = line.push(
|
||||||
text_input("Тэги", &word.tags)
|
text_input("Тэги", &word.tags)
|
||||||
.size(20)
|
.size(ACCENT_FONT_SIZE)
|
||||||
.width(Length::Fill)
|
.width(Length::Fill)
|
||||||
.on_input(move |string| DictionaryMessage::SetTags(i, string))
|
.on_input(move |string| DictionaryMessage::SetTags(i, string))
|
||||||
.on_submit(DictionaryMessage::SubmitWord(i)),
|
.on_submit(DictionaryMessage::SubmitWord(i)),
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
use crate::dictionary::{split_with_coma};
|
use crate::dictionary::{split_with_coma};
|
||||||
use crate::quiz::Score;
|
use crate::quiz::Score;
|
||||||
use crate::Page::PreviousPage;
|
use crate::{RootMessage};
|
||||||
use crate::{RootMessage, DEFAULT_SPACING};
|
|
||||||
use crate::{NavigatedPage, Page};
|
|
||||||
use iced::border::Radius;
|
use iced::border::Radius;
|
||||||
use iced::widget::container::Style;
|
use iced::widget::container::Style;
|
||||||
use iced::widget::{button, container, row, space, text, text_input, Row};
|
use iced::widget::{button, container, row, space, text, text_input, Row};
|
||||||
@@ -10,6 +8,9 @@ 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::WordData;
|
use crate::lang::WordData;
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::PreviousPage;
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct DictionaryQuizState {
|
pub struct DictionaryQuizState {
|
||||||
@@ -84,7 +85,7 @@ impl DictionaryQuizState {
|
|||||||
} else {
|
} else {
|
||||||
String::new()
|
String::new()
|
||||||
}
|
}
|
||||||
).size(20).align_y(alignment::Vertical::Center),
|
).size(ACCENT_FONT_SIZE).align_y(alignment::Vertical::Center),
|
||||||
]
|
]
|
||||||
.align_x(alignment::Horizontal::Center)
|
.align_x(alignment::Horizontal::Center)
|
||||||
.spacing(5),
|
.spacing(5),
|
||||||
|
|||||||
+4
-2
@@ -1,12 +1,14 @@
|
|||||||
use crate::data_provider::history::{HistoryItem, get_history_of_set};
|
use crate::data_provider::history::{HistoryItem, get_history_of_set};
|
||||||
use crate::lang::WordData;
|
use crate::lang::WordData;
|
||||||
use crate::{AppState, DEFAULT_SPACING, NavigatedPage, Page, RootMessage};
|
use crate::{AppState, RootMessage};
|
||||||
use HistoryMessage::Back;
|
use HistoryMessage::Back;
|
||||||
use iced::alignment::Horizontal::Center;
|
use iced::alignment::Horizontal::Center;
|
||||||
use iced::widget::space::horizontal;
|
use iced::widget::space::horizontal;
|
||||||
use iced::widget::*;
|
use iced::widget::*;
|
||||||
use iced::{Element, Fill, FillPortion, Left, Task};
|
use iced::{Element, Fill, FillPortion, Left, Task};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct HistoryState {
|
pub struct HistoryState {
|
||||||
@@ -47,7 +49,7 @@ impl HistoryState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, message: HistoryMessage) -> Task<RootMessage> {
|
pub fn update(&mut self, _: HistoryMessage) -> Task<RootMessage> {
|
||||||
Task::none()
|
Task::none()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
-174
@@ -1,4 +1,4 @@
|
|||||||
#![windows_subsystem = "windows"]
|
// #![windows_subsystem = "windows"]
|
||||||
mod data_provider;
|
mod data_provider;
|
||||||
mod dictionary;
|
mod dictionary;
|
||||||
mod dictionary_test;
|
mod dictionary_test;
|
||||||
@@ -12,47 +12,37 @@ mod sync;
|
|||||||
mod word;
|
mod word;
|
||||||
mod writing;
|
mod writing;
|
||||||
mod history;
|
mod history;
|
||||||
|
pub mod navigation;
|
||||||
|
pub mod styling;
|
||||||
|
|
||||||
use crate::data_provider::card_sets::load_sets;
|
use crate::data_provider::card_sets::load_sets;
|
||||||
use crate::data_provider::settings::get_setting;
|
use crate::data_provider::settings::get_setting;
|
||||||
use crate::data_provider::words::{load_word_groups, load_words};
|
use crate::data_provider::words::{load_word_groups, load_words};
|
||||||
use crate::dictionary::{app_data_dir, DictionaryMessage, DictionaryState};
|
use crate::dictionary::app_data_dir;
|
||||||
use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState};
|
|
||||||
use crate::lang::{WordData, WordGroup};
|
use crate::lang::{WordData, WordGroup};
|
||||||
|
use crate::navigation::{AppSettings, RootMessage, ScreenState};
|
||||||
use crate::quiz::*;
|
use crate::quiz::*;
|
||||||
use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState};
|
use crate::repetitions::CardSetSettings;
|
||||||
use crate::repetition::{RepetitionMessage, RepetitionState};
|
|
||||||
use crate::repetitions::{CardSetSettings, RepetitionsMessage, RepetitionsState};
|
|
||||||
use crate::selector::*;
|
|
||||||
use crate::sync::{SyncMessage, SyncState};
|
|
||||||
use crate::word::{WordMessage, WordState};
|
|
||||||
use crate::writing::{WritingMessage, WritingState};
|
|
||||||
use crate::Page::{Dictionary, DictionaryQuiz, History, Quiz, Randomizer, Repetition, Repetitions, Selector, Sync, Word, Writing};
|
|
||||||
use crate::RootMessage::Keyboard;
|
use crate::RootMessage::Keyboard;
|
||||||
use iced::keyboard::Event;
|
use fontdb::Database;
|
||||||
use iced::widget::text;
|
use iced::{keyboard, Program, Subscription, Theme};
|
||||||
use iced::{keyboard, Element, Program, Subscription, Theme};
|
use iced::Font;
|
||||||
use iced::{Font, Task};
|
|
||||||
use rusqlite::Connection;
|
use rusqlite::Connection;
|
||||||
use std::sync::{Arc, Mutex};
|
|
||||||
use crate::data_provider::sqlite::create_db;
|
|
||||||
use crate::history::{HistoryMessage, HistoryState};
|
|
||||||
|
|
||||||
const DEFAULT_SPACING: f32 = 10.0;
|
|
||||||
|
|
||||||
const USER_FONT: Font = Font::with_name("Noto Sans JP");
|
const USER_FONT: Font = Font::with_name("Noto Sans JP");
|
||||||
|
|
||||||
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)
|
.subscription(subscription)
|
||||||
.title("Kana learn app")
|
.title("Kana learn app")
|
||||||
// .settings(iced::Settings{
|
.settings(iced::Settings{
|
||||||
// default_text_size: iced::Pixels(18.0),
|
default_text_size: iced::Pixels(18.0),
|
||||||
// ..iced::Settings::default()
|
..iced::Settings::default()
|
||||||
// })
|
})
|
||||||
.font(include_bytes!("../noto.ttf"))
|
.font(include_bytes!("../noto.ttf"))
|
||||||
.default_font(USER_FONT)
|
.default_font(USER_FONT)
|
||||||
|
|
||||||
.theme(Theme::GruvboxDark)
|
.theme(Theme::GruvboxDark)
|
||||||
.run()
|
.run()
|
||||||
}
|
}
|
||||||
@@ -61,40 +51,7 @@ fn subscription(_state: &ScreenState) -> Subscription<RootMessage> {
|
|||||||
keyboard::listen().map(|e| Keyboard(e))
|
keyboard::listen().map(|e| Keyboard(e))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub enum RootMessage {
|
|
||||||
Selector(SelectorMessage),
|
|
||||||
Quiz(QuizMessage),
|
|
||||||
Writing(WritingMessage),
|
|
||||||
Dictionary(DictionaryMessage),
|
|
||||||
DictionaryQuiz(DictionaryQuizMessage),
|
|
||||||
Randomizer(RandomizerMessage),
|
|
||||||
Repetitions(RepetitionsMessage),
|
|
||||||
Repetition(RepetitionMessage),
|
|
||||||
Word(WordMessage),
|
|
||||||
Sync(SyncMessage),
|
|
||||||
History(HistoryMessage),
|
|
||||||
Keyboard(Event),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum Page {
|
|
||||||
Selector(SelectorState),
|
|
||||||
Quiz(QuizState),
|
|
||||||
Writing(WritingState),
|
|
||||||
Dictionary(DictionaryState),
|
|
||||||
DictionaryQuiz(DictionaryQuizState),
|
|
||||||
Randomizer(RandomizerState),
|
|
||||||
Repetitions(RepetitionsState),
|
|
||||||
Repetition(RepetitionState),
|
|
||||||
Word(WordState),
|
|
||||||
Sync(SyncState),
|
|
||||||
History(HistoryState),
|
|
||||||
PreviousPage,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ScreenState {
|
|
||||||
stack: Vec<Page>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub dictionary: Vec<WordData>,
|
pub dictionary: Vec<WordData>,
|
||||||
@@ -121,16 +78,7 @@ impl AppState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ScreenState {
|
|
||||||
fn default() -> Self {
|
|
||||||
let mut state = AppState::new();
|
|
||||||
fill_state(&mut state);
|
|
||||||
let state = Arc::new(Mutex::new(state));
|
|
||||||
ScreenState {
|
|
||||||
stack: vec![Selector(SelectorState::new(state.clone()))],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill_state(state: &mut AppState) {
|
fn fill_state(state: &mut AppState) {
|
||||||
let list = load_words(&state.connection);
|
let list = load_words(&state.connection);
|
||||||
@@ -149,110 +97,3 @@ fn load_settings(connection: &Connection) -> AppSettings {
|
|||||||
AppSettings { key }
|
AppSettings { key }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ScreenState {
|
|
||||||
pub fn boot() -> (ScreenState, Task<RootMessage>) {
|
|
||||||
create_db();
|
|
||||||
(ScreenState::default(), Task::none())
|
|
||||||
}
|
|
||||||
pub fn update(&mut self, message: RootMessage) -> Task<RootMessage> {
|
|
||||||
if let Keyboard(e) = message {
|
|
||||||
return self.handle_keyboard(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
state_update!(
|
|
||||||
message,
|
|
||||||
self.stack,
|
|
||||||
Selector,
|
|
||||||
Quiz,
|
|
||||||
Writing,
|
|
||||||
Dictionary,
|
|
||||||
DictionaryQuiz,
|
|
||||||
Randomizer,
|
|
||||||
Repetitions,
|
|
||||||
Repetition,
|
|
||||||
Word,
|
|
||||||
Sync,
|
|
||||||
History
|
|
||||||
);
|
|
||||||
Task::none()
|
|
||||||
}
|
|
||||||
pub fn view(&self) -> Element<'_, RootMessage> {
|
|
||||||
view_navigation!(
|
|
||||||
self.stack,
|
|
||||||
Quiz,
|
|
||||||
Selector,
|
|
||||||
Writing,
|
|
||||||
Dictionary,
|
|
||||||
DictionaryQuiz,
|
|
||||||
Randomizer,
|
|
||||||
Repetitions,
|
|
||||||
Repetition,
|
|
||||||
Word,
|
|
||||||
Sync,
|
|
||||||
History
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AppSettings {
|
|
||||||
key: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! view_navigation {
|
|
||||||
($stack:expr, $($e:ident), *) => {
|
|
||||||
match &$stack.last().unwrap() {
|
|
||||||
$(
|
|
||||||
$e(s) => s.view().map(RootMessage::$e),
|
|
||||||
)*
|
|
||||||
_ => text!("").into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! state_update {
|
|
||||||
($message:expr, $stack:expr, $($e:ident), *) => {
|
|
||||||
match $message {
|
|
||||||
$(
|
|
||||||
RootMessage::$e(msg) => {
|
|
||||||
if let $e(s) = $stack.last_mut().unwrap() {
|
|
||||||
message_navigation!(msg, $stack, s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! message_navigation {
|
|
||||||
($msg:expr, $stack:expr, $state:expr) => {
|
|
||||||
if let Some(new_page) = $state.navigate(&$msg) {
|
|
||||||
if let Page::PreviousPage = new_page {
|
|
||||||
$stack.pop();
|
|
||||||
return Task::none();
|
|
||||||
}
|
|
||||||
$stack.push(new_page);
|
|
||||||
} else {
|
|
||||||
return $state.update($msg);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
trait NavigatedPage<T> {
|
|
||||||
fn navigate(&self, message: &T) -> Option<Page>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait KeyPressedPage {
|
|
||||||
fn press(&mut self, message: &Event) -> Task<RootMessage>;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,179 @@
|
|||||||
|
use crate::message_navigation;
|
||||||
|
use crate::state_update;
|
||||||
|
use crate::view_navigation;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
|
use iced::{Element, Task};
|
||||||
|
use iced::keyboard::Event;
|
||||||
|
use crate::{fill_state, AppState };
|
||||||
|
use crate::data_provider::sqlite::create_db;
|
||||||
|
use crate::dictionary::{DictionaryMessage, DictionaryState};
|
||||||
|
use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState};
|
||||||
|
use crate::history::{HistoryMessage, HistoryState};
|
||||||
|
use crate::navigation::Page::*;
|
||||||
|
use crate::navigation::RootMessage::Keyboard;
|
||||||
|
use crate::quiz::{QuizMessage, QuizState};
|
||||||
|
use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState};
|
||||||
|
use crate::repetition::{RepetitionMessage, RepetitionState};
|
||||||
|
use crate::repetitions::{RepetitionsMessage, RepetitionsState};
|
||||||
|
use crate::selector::{SelectorMessage, SelectorState};
|
||||||
|
use crate::sync::{SyncMessage, SyncState};
|
||||||
|
use crate::word::{WordMessage, WordState};
|
||||||
|
use crate::writing::{WritingMessage, WritingState};
|
||||||
|
|
||||||
|
|
||||||
|
impl Default for ScreenState {
|
||||||
|
fn default() -> Self {
|
||||||
|
let mut state = AppState::new();
|
||||||
|
fill_state(&mut state);
|
||||||
|
let state = Arc::new(Mutex::new(state));
|
||||||
|
ScreenState {
|
||||||
|
stack: vec![Page::Selector(SelectorState::new(state.clone()))],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum RootMessage {
|
||||||
|
Selector(SelectorMessage),
|
||||||
|
Quiz(QuizMessage),
|
||||||
|
Writing(WritingMessage),
|
||||||
|
Dictionary(DictionaryMessage),
|
||||||
|
DictionaryQuiz(DictionaryQuizMessage),
|
||||||
|
Randomizer(RandomizerMessage),
|
||||||
|
Repetitions(RepetitionsMessage),
|
||||||
|
Repetition(RepetitionMessage),
|
||||||
|
Word(WordMessage),
|
||||||
|
Sync(SyncMessage),
|
||||||
|
History(HistoryMessage),
|
||||||
|
Keyboard(Event),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Page {
|
||||||
|
Selector(SelectorState),
|
||||||
|
Quiz(QuizState),
|
||||||
|
Writing(WritingState),
|
||||||
|
Dictionary(DictionaryState),
|
||||||
|
DictionaryQuiz(DictionaryQuizState),
|
||||||
|
Randomizer(RandomizerState),
|
||||||
|
Repetitions(RepetitionsState),
|
||||||
|
Repetition(RepetitionState),
|
||||||
|
Word(WordState),
|
||||||
|
Sync(SyncState),
|
||||||
|
History(HistoryState),
|
||||||
|
PreviousPage,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct ScreenState {
|
||||||
|
stack: Vec<Page>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ScreenState {
|
||||||
|
pub fn boot() -> (ScreenState, Task<RootMessage>) {
|
||||||
|
create_db();
|
||||||
|
(ScreenState::default(), Task::none())
|
||||||
|
}
|
||||||
|
pub fn update(&mut self, message: RootMessage) -> Task<RootMessage> {
|
||||||
|
if let Keyboard(e) = message {
|
||||||
|
return self.handle_keyboard(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
state_update!(
|
||||||
|
message,
|
||||||
|
self.stack,
|
||||||
|
Selector,
|
||||||
|
Quiz,
|
||||||
|
Writing,
|
||||||
|
Dictionary,
|
||||||
|
DictionaryQuiz,
|
||||||
|
Randomizer,
|
||||||
|
Repetitions,
|
||||||
|
Repetition,
|
||||||
|
Word,
|
||||||
|
Sync,
|
||||||
|
History
|
||||||
|
);
|
||||||
|
Task::none()
|
||||||
|
}
|
||||||
|
pub fn view(&self) -> Element<'_, RootMessage> {
|
||||||
|
view_navigation!(
|
||||||
|
self.stack,
|
||||||
|
Quiz,
|
||||||
|
Selector,
|
||||||
|
Writing,
|
||||||
|
Dictionary,
|
||||||
|
DictionaryQuiz,
|
||||||
|
Randomizer,
|
||||||
|
Repetitions,
|
||||||
|
Repetition,
|
||||||
|
Word,
|
||||||
|
Sync,
|
||||||
|
History
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn handle_keyboard(&mut self, message: Event) -> Task<RootMessage> {
|
||||||
|
let page = self.stack.last_mut().unwrap();
|
||||||
|
match page {
|
||||||
|
Page::Repetition(page) => page.press(&message),
|
||||||
|
_ => Task::none(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AppSettings {
|
||||||
|
pub(crate) key: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! view_navigation {
|
||||||
|
($stack:expr, $($e:ident), *) => {
|
||||||
|
match &$stack.last().unwrap() {
|
||||||
|
$(
|
||||||
|
$e(s) => s.view().map(RootMessage::$e),
|
||||||
|
)*
|
||||||
|
_ => iced::widget::space().into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! state_update {
|
||||||
|
($message:expr, $stack:expr, $($e:ident), *) => {
|
||||||
|
match $message {
|
||||||
|
$(
|
||||||
|
RootMessage::$e(msg) => {
|
||||||
|
if let $e(s) = $stack.last_mut().unwrap() {
|
||||||
|
message_navigation!(msg, $stack, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)*
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! message_navigation {
|
||||||
|
($msg:expr, $stack:expr, $state:expr) => {
|
||||||
|
if let Some(new_page) = $state.navigate(&$msg) {
|
||||||
|
if let Page::PreviousPage = new_page {
|
||||||
|
$stack.pop();
|
||||||
|
return Task::none();
|
||||||
|
}
|
||||||
|
$stack.push(new_page);
|
||||||
|
} else {
|
||||||
|
return $state.update($msg);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait NavigatedPage<T> {
|
||||||
|
fn navigate(&self, message: &T) -> Option<Page>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait KeyPressedPage {
|
||||||
|
fn press(&mut self, message: &Event) -> Task<RootMessage>;
|
||||||
|
}
|
||||||
+4
-2
@@ -1,9 +1,11 @@
|
|||||||
use crate::lang::KanaSet;
|
use crate::lang::KanaSet;
|
||||||
use crate::Page::PreviousPage;
|
use crate::{ RootMessage, USER_FONT};
|
||||||
use crate::{NavigatedPage, Page, RootMessage, DEFAULT_SPACING, USER_FONT};
|
|
||||||
use iced::widget::*;
|
use iced::widget::*;
|
||||||
use iced::{alignment, Element, Fill, Task};
|
use iced::{alignment, Element, Fill, Task};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::PreviousPage;
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct QuizState {
|
pub struct QuizState {
|
||||||
|
|||||||
+2
-1
@@ -1,10 +1,11 @@
|
|||||||
|
|
||||||
pub mod randomizer {
|
pub mod randomizer {
|
||||||
use crate::randomizer::randomizer::RandomizerMessage::{Back, Start, Edit};
|
use crate::randomizer::randomizer::RandomizerMessage::{Back, Start, Edit};
|
||||||
use crate::{NavigatedPage, Page, RootMessage};
|
use crate::{RootMessage};
|
||||||
use iced::widget::{button, container, text_editor};
|
use iced::widget::{button, container, text_editor};
|
||||||
use iced::Task;
|
use iced::Task;
|
||||||
use rand::prelude::SliceRandom;
|
use rand::prelude::SliceRandom;
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RandomizerState {
|
pub struct RandomizerState {
|
||||||
|
|||||||
+9
-6
@@ -1,16 +1,19 @@
|
|||||||
use crate::data_provider::voice::get_voice;
|
use crate::data_provider::voice::get_voice;
|
||||||
use crate::lang::{CardSet, CardStatistics, WordData, WordOpenMode};
|
use crate::lang::{CardSet, CardStatistics, WordData, WordOpenMode};
|
||||||
use crate::repetitions::CardSetSettings;
|
use crate::repetitions::CardSetSettings;
|
||||||
use crate::Page::PreviousPage;
|
use crate::{AppState, RootMessage};
|
||||||
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, Column};
|
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, Theme};
|
||||||
use rodio::MixerDeviceSink;
|
use rodio::MixerDeviceSink;
|
||||||
use std::collections::HashSet;
|
use std::collections::HashSet;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use iced::widget::text::Style;
|
||||||
use tokio::task::spawn_blocking;
|
use tokio::task::spawn_blocking;
|
||||||
|
use crate::navigation::{KeyPressedPage, NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::PreviousPage;
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
pub struct RepetitionState {
|
pub struct RepetitionState {
|
||||||
pub settings: CardSetSettings,
|
pub settings: CardSetSettings,
|
||||||
@@ -128,7 +131,7 @@ impl RepetitionState {
|
|||||||
container(self.answer_bar())
|
container(self.answer_bar())
|
||||||
.width(Fill)
|
.width(Fill)
|
||||||
.align_x(Center)
|
.align_x(Center)
|
||||||
.height(60),
|
.height(80),
|
||||||
text!(
|
text!(
|
||||||
"Затронуто слов {}, {}%",
|
"Затронуто слов {}, {}%",
|
||||||
self.opened.len(),
|
self.opened.len(),
|
||||||
@@ -182,12 +185,12 @@ impl RepetitionState {
|
|||||||
|
|
||||||
fn answer_bar(&self) -> Element<'_, RepetitionMessage> {
|
fn answer_bar(&self) -> Element<'_, RepetitionMessage> {
|
||||||
if !self.open {
|
if !self.open {
|
||||||
return space().into();
|
return text!("Нажмите Пробел для открытия карточки").style(|theme: &Theme| {Style{ color: Some(theme.palette().text.scale_alpha(0.3)) }}).into();
|
||||||
}
|
}
|
||||||
|
|
||||||
column![
|
column![
|
||||||
text!("{} очков", self.current_statistic.calculated_score().round() as i32),
|
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)),
|
||||||
button("Нормально").on_press(RepetitionMessage::Answer(WordOpenMode::Ok)),
|
button("Нормально").on_press(RepetitionMessage::Answer(WordOpenMode::Ok)),
|
||||||
|
|||||||
+14
-9
@@ -2,16 +2,18 @@ use crate::data_provider::card_sets::{delete_set, update_card_set};
|
|||||||
use crate::data_provider::card_stats::load_stats_of_set;
|
use crate::data_provider::card_stats::load_stats_of_set;
|
||||||
use crate::lang::{SetOrderMode, WordData};
|
use crate::lang::{SetOrderMode, WordData};
|
||||||
use crate::repetition::RepetitionState;
|
use crate::repetition::RepetitionState;
|
||||||
use crate::Page::{History, PreviousPage, Repetition};
|
use crate::{AppState, RootMessage};
|
||||||
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
|
use iced::widget::button::{danger, Status};
|
||||||
use iced::widget::button::danger;
|
|
||||||
pub use iced::widget::button::{Catalog, Style};
|
pub use iced::widget::button::{Catalog, Style};
|
||||||
use iced::widget::container::bordered_box;
|
use iced::widget::container::bordered_box;
|
||||||
use iced::widget::{button, column, container, radio, row, scrollable, space, text, text_input, Column};
|
use iced::widget::{button, column, container, radio, row, scrollable, space, text, text_input, Column};
|
||||||
use iced::{Border, Center, Element, Fill, Left, Length, Shadow, Task, Theme};
|
use iced::{color, Background, Border, Center, Color, Element, Fill, Left, Length, Shadow, Task, Theme};
|
||||||
use rhai::{Engine, Scope};
|
use rhai::{Engine, Scope};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use crate::history::HistoryState;
|
use crate::history::HistoryState;
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::{History, PreviousPage, Repetition};
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct RepetitionsState {
|
pub struct RepetitionsState {
|
||||||
@@ -134,7 +136,6 @@ impl RepetitionsState {
|
|||||||
self.launch_button()
|
self.launch_button()
|
||||||
]
|
]
|
||||||
.align_y(Center)
|
.align_y(Center)
|
||||||
.padding(10)
|
|
||||||
.spacing(DEFAULT_SPACING)
|
.spacing(DEFAULT_SPACING)
|
||||||
.width(Fill)
|
.width(Fill)
|
||||||
.height(Fill)
|
.height(Fill)
|
||||||
@@ -148,7 +149,7 @@ impl RepetitionsState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn launch_button(&self) -> Element<'_, RepetitionsMessage> {
|
fn launch_button(&self) -> Element<'_, RepetitionsMessage> {
|
||||||
if let Some(_) = self.selected_set {
|
if let Some(set) = self.selected_set && self.state.lock().unwrap().card_sets[set].id != 0 {
|
||||||
return button(text!("▷").height(Fill).center())
|
return button(text!("▷").height(Fill).center())
|
||||||
.height(200)
|
.height(200)
|
||||||
.on_press(RepetitionsMessage::GoToRepetition)
|
.on_press(RepetitionsMessage::GoToRepetition)
|
||||||
@@ -235,14 +236,18 @@ impl RepetitionsState {
|
|||||||
column = column.push(
|
column = column.push(
|
||||||
button(text!("{}", set.name.clone()))
|
button(text!("{}", set.name.clone()))
|
||||||
.on_press_with(move || RepetitionsMessage::SelectSet(i.clone()))
|
.on_press_with(move || RepetitionsMessage::SelectSet(i.clone()))
|
||||||
.style(move |_x: &Theme, _status| Style {
|
.style(move |_x: &Theme, status : Status| Style {
|
||||||
background: None,
|
background: if status == Status::Hovered {Some(Background::Color(Color::WHITE.scale_alpha(0.1)))} else { None },
|
||||||
text_color: if self.correct_filters[i.clone()] {
|
text_color: if self.correct_filters[i.clone()] {
|
||||||
_x.palette().primary
|
_x.palette().primary
|
||||||
} else {
|
} else {
|
||||||
_x.palette().warning
|
_x.palette().warning
|
||||||
},
|
},
|
||||||
border: Border::default(),
|
border: Border{
|
||||||
|
color: Default::default(),
|
||||||
|
width: 0.0,
|
||||||
|
radius: 8.0.into(),
|
||||||
|
},
|
||||||
shadow: Shadow::default(),
|
shadow: Shadow::default(),
|
||||||
snap: false,
|
snap: false,
|
||||||
}),
|
}),
|
||||||
|
|||||||
+4
-2
@@ -5,10 +5,12 @@ use crate::randomizer::randomizer::RandomizerState;
|
|||||||
use crate::repetitions::RepetitionsState;
|
use crate::repetitions::RepetitionsState;
|
||||||
use crate::selector::SelectorMessage::ChangeMode;
|
use crate::selector::SelectorMessage::ChangeMode;
|
||||||
use crate::writing::WritingState;
|
use crate::writing::WritingState;
|
||||||
use crate::Page::{Quiz, Writing};
|
use crate::{AppState, QuizState, RootMessage};
|
||||||
use crate::{AppState, NavigatedPage, Page, QuizState, RootMessage, DEFAULT_SPACING};
|
|
||||||
use iced::widget::*;
|
use iced::widget::*;
|
||||||
use iced::{alignment, Element, Task};
|
use iced::{alignment, Element, Task};
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::{Quiz, Writing};
|
||||||
|
use crate::styling::*;
|
||||||
use crate::sync::SyncState;
|
use crate::sync::SyncState;
|
||||||
|
|
||||||
pub struct SelectorState {
|
pub struct SelectorState {
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
use iced::border::Radius;
|
||||||
|
use iced::widget::button::{primary, Status};
|
||||||
|
use iced::widget::button::danger;
|
||||||
|
|
||||||
|
use iced::widget::{button, Button};
|
||||||
|
use iced::Element;
|
||||||
|
use iced_core::{Background, Border, Theme};
|
||||||
|
use crate::repetitions::Style;
|
||||||
|
|
||||||
|
pub const DEFAULT_SPACING: f32 = 16.0;
|
||||||
|
pub const ACCENT_FONT_SIZE: f32 = 22.0;
|
||||||
|
const BUTTON_RADIUS: f32 = 8.0;
|
||||||
|
|
||||||
|
|
||||||
|
pub fn jl_button(theme: &Theme, status: Status) -> Style {
|
||||||
|
let mut style = primary(theme, status);
|
||||||
|
style.border = Border
|
||||||
|
{
|
||||||
|
color: Default::default(),
|
||||||
|
width: 0.0,
|
||||||
|
radius: Radius::new(BUTTON_RADIUS),
|
||||||
|
};
|
||||||
|
style
|
||||||
|
}
|
||||||
+10
-6
@@ -1,16 +1,20 @@
|
|||||||
use crate::data_provider::settings::{delete_settings, set_setting};
|
use crate::data_provider::settings::{delete_settings, set_setting};
|
||||||
use crate::dictionary::app_data_dir;
|
use crate::dictionary::app_data_dir;
|
||||||
use crate::sync::SyncMessage::NextAnimation;
|
use crate::sync::SyncMessage::NextAnimation;
|
||||||
use crate::Page::PreviousPage;
|
use crate::{fill_state, AppState, RootMessage};
|
||||||
use crate::{fill_state, AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
|
|
||||||
use iced::widget::button::danger;
|
use iced::widget::button::danger;
|
||||||
use iced::widget::container::rounded_box;
|
use iced::widget::container::rounded_box;
|
||||||
use iced::widget::{button, column, container, progress_bar, row, space, text};
|
use iced::widget::{button, column, container, progress_bar, row, scrollable, space, text};
|
||||||
use iced::{Center, Element, Fill, Font, Left, Task};
|
use iced::{Center, Element, Fill, Font, Left, Length, Task};
|
||||||
use std::io;
|
use std::io;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use iced::border::width;
|
||||||
|
use iced::widget::operation::scroll_to;
|
||||||
use zstd::{Decoder, Encoder, DEFAULT_COMPRESSION_LEVEL};
|
use zstd::{Decoder, Encoder, DEFAULT_COMPRESSION_LEVEL};
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::PreviousPage;
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
const API_URL: &str = "https://learning.micialware.ru/";
|
const API_URL: &str = "https://learning.micialware.ru/";
|
||||||
/*const API_URL: &str = "http://localhost:8089/";*/
|
/*const API_URL: &str = "http://localhost:8089/";*/
|
||||||
@@ -181,7 +185,7 @@ impl SyncState {
|
|||||||
column![
|
column![
|
||||||
text!("Ваш ключ синхронизации"),
|
text!("Ваш ключ синхронизации"),
|
||||||
container(
|
container(
|
||||||
container(text!("{}", key).size(20).font(Font::MONOSPACE)).padding(3)
|
container(text!("{}", key).size(ACCENT_FONT_SIZE).font(Font::MONOSPACE)).padding(3)
|
||||||
)
|
)
|
||||||
|
|
||||||
.style(rounded_box),
|
.style(rounded_box),
|
||||||
@@ -208,7 +212,7 @@ impl SyncState {
|
|||||||
}
|
}
|
||||||
.spacing(DEFAULT_SPACING)
|
.spacing(DEFAULT_SPACING)
|
||||||
.align_x(Center)
|
.align_x(Center)
|
||||||
.width(300)
|
.width(Length::Shrink)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -1,11 +1,13 @@
|
|||||||
use crate::data_provider::words::{delete_word, update_word};
|
use crate::data_provider::words::{delete_word, update_word};
|
||||||
use crate::lang::WordData;
|
use crate::lang::WordData;
|
||||||
use crate::Page::PreviousPage;
|
use crate::{AppState, RootMessage};
|
||||||
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
|
|
||||||
use iced::widget::button::danger;
|
use iced::widget::button::danger;
|
||||||
use iced::widget::{button, column, container, row, rule, scrollable, space, text, text_input};
|
use iced::widget::{button, column, container, row, rule, scrollable, space, text, text_input};
|
||||||
use iced::{Element, Fill, Task};
|
use iced::{Element, Fill, Task};
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::PreviousPage;
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct WordState {
|
pub struct WordState {
|
||||||
|
|||||||
+4
-2
@@ -1,9 +1,11 @@
|
|||||||
use crate::lang::KanaSet;
|
use crate::lang::KanaSet;
|
||||||
use crate::Page::PreviousPage;
|
use crate::{ RootMessage};
|
||||||
use crate::{NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
|
|
||||||
use iced::widget::*;
|
use iced::widget::*;
|
||||||
use iced::{alignment, Element, Fill, Task};
|
use iced::{alignment, Element, Fill, Task};
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
|
use crate::navigation::{NavigatedPage, Page};
|
||||||
|
use crate::navigation::Page::PreviousPage;
|
||||||
|
use crate::styling::*;
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct WritingState {
|
pub struct WritingState {
|
||||||
|
|||||||
Reference in New Issue
Block a user