2026-02-10 17:25:39 +03:00
|
|
|
#![windows_subsystem = "windows"]
|
2026-02-10 11:28:12 +03:00
|
|
|
mod lang;
|
2026-02-10 12:12:16 +03:00
|
|
|
mod quiz;
|
|
|
|
|
mod selector;
|
2026-02-10 17:04:48 +03:00
|
|
|
mod writing;
|
2026-02-10 11:28:12 +03:00
|
|
|
|
2026-02-10 12:12:16 +03:00
|
|
|
use crate::quiz::*;
|
|
|
|
|
use crate::selector::*;
|
2026-02-10 17:30:43 +03:00
|
|
|
use crate::writing::{WritingMessage, WritingState};
|
2026-02-10 17:04:48 +03:00
|
|
|
use crate::Page::{Quiz, Selector, Writing};
|
2026-02-10 12:12:16 +03:00
|
|
|
use iced::widget::text;
|
|
|
|
|
use iced::Element;
|
2026-02-10 11:28:12 +03:00
|
|
|
|
|
|
|
|
fn main() -> iced::Result {
|
|
|
|
|
iced::application("A kana learn app", ScreenState::update, ScreenState::view).run()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
|
pub enum RootMessage {
|
|
|
|
|
Selector(SelectorMessage),
|
|
|
|
|
Quiz(QuizMessage),
|
2026-02-10 17:04:48 +03:00
|
|
|
Writing(WritingMessage),
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
enum Page {
|
|
|
|
|
Selector(SelectorState),
|
|
|
|
|
Quiz(QuizState),
|
2026-02-10 17:04:48 +03:00
|
|
|
Writing(WritingState),
|
2026-02-10 11:28:12 +03:00
|
|
|
Back,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for Page {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Selector(SelectorState::default())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct ScreenState {
|
|
|
|
|
stack: Vec<Page>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Default for ScreenState {
|
|
|
|
|
fn default() -> Self {
|
|
|
|
|
ScreenState {
|
|
|
|
|
stack: vec![Page::default()],
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl ScreenState {
|
|
|
|
|
pub fn update(&mut self, message: RootMessage) {
|
2026-02-10 17:04:48 +03:00
|
|
|
state_update!(message, self.stack, Selector, Quiz, Writing);
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn view(&self) -> Element<'_, RootMessage> {
|
2026-02-10 17:04:48 +03:00
|
|
|
view_navigation!(self.stack, Quiz, Selector, Writing)
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-10 12:12:16 +03:00
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! view_navigation {
|
|
|
|
|
($stack:expr, $($e:ident), *) => {
|
|
|
|
|
match &$stack.last().unwrap() {
|
|
|
|
|
$(
|
|
|
|
|
$e(s) => s.view().map(RootMessage::$e),
|
|
|
|
|
)*
|
|
|
|
|
_ => text!("").into(),
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-10 12:12:16 +03:00
|
|
|
#[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)
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
|
|
|
|
}
|
2026-02-10 12:12:16 +03:00
|
|
|
)*
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-10 12:12:16 +03:00
|
|
|
#[macro_export]
|
|
|
|
|
macro_rules! message_navigation {
|
|
|
|
|
($msg:expr, $stack:expr, $state:expr) => {
|
|
|
|
|
if let Some(new_page) = $state.navigate(&$msg) {
|
|
|
|
|
if let Page::Back = new_page {
|
|
|
|
|
$stack.pop();
|
|
|
|
|
return;
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
2026-02-10 12:12:16 +03:00
|
|
|
$stack.push(new_page);
|
|
|
|
|
} else {
|
|
|
|
|
$state.update($msg);
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|
2026-02-10 12:12:16 +03:00
|
|
|
};
|
|
|
|
|
}
|
2026-02-10 11:28:12 +03:00
|
|
|
|
2026-02-10 12:12:16 +03:00
|
|
|
trait NavigatedPage<T> {
|
|
|
|
|
fn navigate(&self, message: &T) -> Option<Page>;
|
2026-02-10 11:28:12 +03:00
|
|
|
}
|