Before button global styling

This commit is contained in:
2026-08-01 13:04:18 +03:00
parent ce94b01543
commit d9bd1b1b86
16 changed files with 304 additions and 218 deletions
+7 -5
View File
@@ -5,8 +5,7 @@ use crate::dictionary::DictionaryMessage::{
use crate::dictionary_test::DictionaryQuizState;
use crate::lang::{WordData, WordGroup};
use crate::word::WordState;
use crate::Page::Word;
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use crate::{AppState, RootMessage };
use chrono::{DateTime, TimeDelta, Utc};
use iced::alignment::Vertical::Center;
use iced::widget::button::Style;
@@ -22,6 +21,9 @@ use std::path::PathBuf;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use DictionaryMessage::Back;
use crate::navigation::{NavigatedPage, Page};
use crate::navigation::Page::Word;
use crate::styling::*;
#[derive(Clone)]
pub struct DictionaryState {
@@ -347,21 +349,21 @@ impl DictionaryState {
line = line.push(
text_input("Слово", &word.key)
.size(20)
.size(ACCENT_FONT_SIZE)
.width(Length::Fill)
.on_input(move |string| DictionaryMessage::SetKey(i, string))
.on_submit(DictionaryMessage::SubmitWord(i)),
);
line = line.push(
text_input("Перевод", &word.value)
.size(20)
.size(ACCENT_FONT_SIZE)
.width(Length::Fill)
.on_input(move |string| DictionaryMessage::SetValue(i, string))
.on_submit(DictionaryMessage::SubmitWord(i)),
);
line = line.push(
text_input("Тэги", &word.tags)
.size(20)
.size(ACCENT_FONT_SIZE)
.width(Length::Fill)
.on_input(move |string| DictionaryMessage::SetTags(i, string))
.on_submit(DictionaryMessage::SubmitWord(i)),
+5 -4
View File
@@ -1,8 +1,6 @@
use crate::dictionary::{split_with_coma};
use crate::quiz::Score;
use crate::Page::PreviousPage;
use crate::{RootMessage, DEFAULT_SPACING};
use crate::{NavigatedPage, Page};
use crate::{RootMessage};
use iced::border::Radius;
use iced::widget::container::Style;
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 rand::prelude::SliceRandom;
use crate::lang::WordData;
use crate::navigation::{NavigatedPage, Page};
use crate::navigation::Page::PreviousPage;
use crate::styling::*;
#[derive(Debug, Clone)]
pub struct DictionaryQuizState {
@@ -84,7 +85,7 @@ impl DictionaryQuizState {
} else {
String::new()
}
).size(20).align_y(alignment::Vertical::Center),
).size(ACCENT_FONT_SIZE).align_y(alignment::Vertical::Center),
]
.align_x(alignment::Horizontal::Center)
.spacing(5),
+4 -2
View File
@@ -1,12 +1,14 @@
use crate::data_provider::history::{HistoryItem, get_history_of_set};
use crate::lang::WordData;
use crate::{AppState, DEFAULT_SPACING, NavigatedPage, Page, RootMessage};
use crate::{AppState, RootMessage};
use HistoryMessage::Back;
use iced::alignment::Horizontal::Center;
use iced::widget::space::horizontal;
use iced::widget::*;
use iced::{Element, Fill, FillPortion, Left, Task};
use std::sync::{Arc, Mutex};
use crate::navigation::{NavigatedPage, Page};
use crate::styling::*;
#[derive(Clone)]
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()
}
+16 -175
View File
@@ -1,4 +1,4 @@
#![windows_subsystem = "windows"]
// #![windows_subsystem = "windows"]
mod data_provider;
mod dictionary;
mod dictionary_test;
@@ -12,47 +12,37 @@ mod sync;
mod word;
mod writing;
mod history;
pub mod navigation;
pub mod styling;
use crate::data_provider::card_sets::load_sets;
use crate::data_provider::settings::get_setting;
use crate::data_provider::words::{ load_word_groups, load_words};
use crate::dictionary::{app_data_dir, DictionaryMessage, DictionaryState};
use crate::dictionary_test::{DictionaryQuizMessage, DictionaryQuizState};
use crate::data_provider::words::{load_word_groups, load_words};
use crate::dictionary::app_data_dir;
use crate::lang::{WordData, WordGroup};
use crate::navigation::{AppSettings, RootMessage, ScreenState};
use crate::quiz::*;
use crate::randomizer::randomizer::{RandomizerMessage, RandomizerState};
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::repetitions::CardSetSettings;
use crate::RootMessage::Keyboard;
use iced::keyboard::Event;
use iced::widget::text;
use iced::{keyboard, Element, Program, Subscription, Theme};
use iced::{Font, Task};
use fontdb::Database;
use iced::{keyboard, Program, Subscription, Theme};
use iced::Font;
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");
fn main() -> iced::Result {
iced::application(ScreenState::boot, ScreenState::update, ScreenState::view)
.subscription(subscription)
.title("Kana learn app")
// .settings(iced::Settings{
// default_text_size: iced::Pixels(18.0),
// ..iced::Settings::default()
// })
.settings(iced::Settings{
default_text_size: iced::Pixels(18.0),
..iced::Settings::default()
})
.font(include_bytes!("../noto.ttf"))
.default_font(USER_FONT)
.theme(Theme::GruvboxDark)
.run()
}
@@ -61,40 +51,7 @@ fn subscription(_state: &ScreenState) -> Subscription<RootMessage> {
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 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) {
let list = load_words(&state.connection);
@@ -149,110 +97,3 @@ fn load_settings(connection: &Connection) -> AppSettings {
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>;
}
+179
View File
@@ -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
View File
@@ -1,9 +1,11 @@
use crate::lang::KanaSet;
use crate::Page::PreviousPage;
use crate::{NavigatedPage, Page, RootMessage, DEFAULT_SPACING, USER_FONT};
use crate::{ RootMessage, USER_FONT};
use iced::widget::*;
use iced::{alignment, Element, Fill, Task};
use rand::seq::SliceRandom;
use crate::navigation::{NavigatedPage, Page};
use crate::navigation::Page::PreviousPage;
use crate::styling::*;
#[derive(Clone, Debug)]
pub struct QuizState {
+2 -1
View File
@@ -1,10 +1,11 @@
pub mod randomizer {
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::Task;
use rand::prelude::SliceRandom;
use crate::navigation::{NavigatedPage, Page};
#[derive(Clone, Debug)]
pub struct RandomizerState {
+9 -6
View File
@@ -1,16 +1,19 @@
use crate::data_provider::voice::get_voice;
use crate::lang::{CardSet, CardStatistics, WordData, WordOpenMode};
use crate::repetitions::CardSetSettings;
use crate::Page::PreviousPage;
use crate::{AppState, KeyPressedPage, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use crate::{AppState, RootMessage};
use iced::alignment::Horizontal::Center;
use iced::keyboard::key::Physical::Code;
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 std::collections::HashSet;
use std::sync::{Arc, Mutex};
use iced::widget::text::Style;
use tokio::task::spawn_blocking;
use crate::navigation::{KeyPressedPage, NavigatedPage, Page};
use crate::navigation::Page::PreviousPage;
use crate::styling::*;
pub struct RepetitionState {
pub settings: CardSetSettings,
@@ -128,7 +131,7 @@ impl RepetitionState {
container(self.answer_bar())
.width(Fill)
.align_x(Center)
.height(60),
.height(80),
text!(
"Затронуто слов {}, {}%",
self.opened.len(),
@@ -182,12 +185,12 @@ impl RepetitionState {
fn answer_bar(&self) -> Element<'_, RepetitionMessage> {
if !self.open {
return space().into();
return text!("Нажмите Пробел для открытия карточки").style(|theme: &Theme| {Style{ color: Some(theme.palette().text.scale_alpha(0.3)) }}).into();
}
column![
text!("{} очков", self.current_statistic.calculated_score().round() as i32),
row![
button("Не получилось").on_press(RepetitionMessage::Answer(WordOpenMode::None)),
button("Трудно").on_press(RepetitionMessage::Answer(WordOpenMode::Hard)),
button("Нормально").on_press(RepetitionMessage::Answer(WordOpenMode::Ok)),
+14 -9
View File
@@ -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::lang::{SetOrderMode, WordData};
use crate::repetition::RepetitionState;
use crate::Page::{History, PreviousPage, Repetition};
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use iced::widget::button::danger;
use crate::{AppState, RootMessage};
use iced::widget::button::{danger, Status};
pub use iced::widget::button::{Catalog, Style};
use iced::widget::container::bordered_box;
use iced::widget::{button, column, container, radio, row, scrollable, space, text, text_input, Column};
use iced::{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 std::sync::{Arc, Mutex};
use crate::history::HistoryState;
use crate::navigation::{NavigatedPage, Page};
use crate::navigation::Page::{History, PreviousPage, Repetition};
use crate::styling::*;
#[derive(Clone)]
pub struct RepetitionsState {
@@ -134,7 +136,6 @@ impl RepetitionsState {
self.launch_button()
]
.align_y(Center)
.padding(10)
.spacing(DEFAULT_SPACING)
.width(Fill)
.height(Fill)
@@ -148,7 +149,7 @@ impl RepetitionsState {
}
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())
.height(200)
.on_press(RepetitionsMessage::GoToRepetition)
@@ -235,14 +236,18 @@ impl RepetitionsState {
column = column.push(
button(text!("{}", set.name.clone()))
.on_press_with(move || RepetitionsMessage::SelectSet(i.clone()))
.style(move |_x: &Theme, _status| Style {
background: None,
.style(move |_x: &Theme, status : Status| Style {
background: if status == Status::Hovered {Some(Background::Color(Color::WHITE.scale_alpha(0.1)))} else { None },
text_color: if self.correct_filters[i.clone()] {
_x.palette().primary
} else {
_x.palette().warning
},
border: Border::default(),
border: Border{
color: Default::default(),
width: 0.0,
radius: 8.0.into(),
},
shadow: Shadow::default(),
snap: false,
}),
+4 -2
View File
@@ -5,10 +5,12 @@ use crate::randomizer::randomizer::RandomizerState;
use crate::repetitions::RepetitionsState;
use crate::selector::SelectorMessage::ChangeMode;
use crate::writing::WritingState;
use crate::Page::{Quiz, Writing};
use crate::{AppState, NavigatedPage, Page, QuizState, RootMessage, DEFAULT_SPACING};
use crate::{AppState, QuizState, RootMessage};
use iced::widget::*;
use iced::{alignment, Element, Task};
use crate::navigation::{NavigatedPage, Page};
use crate::navigation::Page::{Quiz, Writing};
use crate::styling::*;
use crate::sync::SyncState;
pub struct SelectorState {
+24
View File
@@ -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
View File
@@ -1,16 +1,20 @@
use crate::data_provider::settings::{delete_settings, set_setting};
use crate::dictionary::app_data_dir;
use crate::sync::SyncMessage::NextAnimation;
use crate::Page::PreviousPage;
use crate::{fill_state, AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use crate::{fill_state, AppState, RootMessage};
use iced::widget::button::danger;
use iced::widget::container::rounded_box;
use iced::widget::{button, column, container, progress_bar, row, space, text};
use iced::{Center, Element, Fill, Font, Left, Task};
use iced::widget::{button, column, container, progress_bar, row, scrollable, space, text};
use iced::{Center, Element, Fill, Font, Left, Length, Task};
use std::io;
use std::sync::{Arc, Mutex};
use std::time::Duration;
use iced::border::width;
use iced::widget::operation::scroll_to;
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 = "http://localhost:8089/";*/
@@ -181,7 +185,7 @@ impl SyncState {
column![
text!("Ваш ключ синхронизации"),
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),
@@ -208,7 +212,7 @@ impl SyncState {
}
.spacing(DEFAULT_SPACING)
.align_x(Center)
.width(300)
.width(Length::Shrink)
.into()
}
+4 -2
View File
@@ -1,11 +1,13 @@
use crate::data_provider::words::{delete_word, update_word};
use crate::lang::WordData;
use crate::Page::PreviousPage;
use crate::{AppState, NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use crate::{AppState, RootMessage};
use iced::widget::button::danger;
use iced::widget::{button, column, container, row, rule, scrollable, space, text, text_input};
use iced::{Element, Fill, Task};
use std::sync::{Arc, Mutex};
use crate::navigation::{NavigatedPage, Page};
use crate::navigation::Page::PreviousPage;
use crate::styling::*;
#[derive(Clone)]
pub struct WordState {
+4 -2
View File
@@ -1,9 +1,11 @@
use crate::lang::KanaSet;
use crate::Page::PreviousPage;
use crate::{NavigatedPage, Page, RootMessage, DEFAULT_SPACING};
use crate::{ RootMessage};
use iced::widget::*;
use iced::{alignment, Element, Fill, Task};
use rand::seq::SliceRandom;
use crate::navigation::{NavigatedPage, Page};
use crate::navigation::Page::PreviousPage;
use crate::styling::*;
#[derive(Clone, Debug)]
pub struct WritingState {