diff --git a/Cargo.toml b/Cargo.toml index 7e0511c..e501abb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,5 @@ eframe = { version = "0.21.3", default-features = false, features = [ "persistence", # Enable restoring app state when restarting the app. ] } tracing-subscriber = "0.3" -serde = { version = "1", features = ["derive"] } \ No newline at end of file +serde = { version = "1", features = ["derive"] } +rand = "0.8.5" \ No newline at end of file diff --git a/justfile b/justfile index ae1cd4b..87e1768 100644 --- a/justfile +++ b/justfile @@ -3,4 +3,7 @@ run: - cargo run \ No newline at end of file + cargo run + +hurl: + hurl basic.hurl \ No newline at end of file diff --git a/src/app.rs b/src/app.rs index f55f293..04c3885 100644 --- a/src/app.rs +++ b/src/app.rs @@ -1,18 +1,18 @@ -use egui::Ui; +use crate::{settings::TFSetting, typewriter::{TypeState, Challenge}, random::random_letters}; /// We derive Deserialize/Serialize so we can persist app state on shutdown. #[derive(serde::Deserialize, serde::Serialize)] #[serde(default)] // if we add new fields, give them default values when deserializing old state pub struct TypeFastApp { - command: String, - settings_open: bool, + settings: TFSetting, + type_state: TypeState } impl Default for TypeFastApp { fn default() -> Self { Self { - command: "".into(), - settings_open: false, + settings: TFSetting::default(), + type_state: TypeState::default(), } } } @@ -46,43 +46,22 @@ impl eframe::App for TypeFastApp { } }); }); - - - }); egui::TopBottomPanel::bottom("bottom_panel_0").show(ctx, |ui| { - ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { - ui.text_edit_singleline(&mut self.command); - process_command(self, ui, ctx); + ui.text_edit_singleline(&mut self.settings.command); + TFSetting::process_command(&mut self.settings, ui, ctx); ui.horizontal(|ui| { - ui.spacing_mut().item_spacing.x= 0.5; - if ui.button("open settings").clicked() { - self.command = "open settings;".into(); - } - if ui.button("close settings").clicked() { - self.command = "close settings;".into(); - } + TFSetting::command_helpers(&mut self.settings, ui); + }); }); }); egui::CentralPanel::default().show(ctx, |ui| { - ui.heading("central"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); - ui.label("placeholder"); + + self.type_state.render(ui, random_letters(32).as_str()) }); } @@ -92,30 +71,10 @@ impl eframe::App for TypeFastApp { } } -fn process_command(app: &mut TypeFastApp, ui: &mut Ui, ctx: &egui::Context) { - let command = app.command.clone(); - if app.command.contains(";") { - app.command.clear(); - } - - if command.eq("") { - ui.label(&app.command); - } - - if command.eq("open settings;") { - app.settings_open = true - } - if command.eq("close settings;") { - app.settings_open = false - } - if app.settings_open { - egui::Window::new("Draft setting").show(ctx, |ui| { - ui.label("Hey"); - if ui.button("close").clicked() { - app.settings_open = false - } - }); +impl Challenge for str { + fn into_challenge(&self) -> String { + self.to_string() } -} +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 455522b..3513a33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,16 +4,43 @@ use crate::app::TypeFastApp; mod app; - +mod settings; +mod typewriter; +mod random; fn main() -> eframe::Result<()> { // Log to stdout (if you run with `RUST_LOG=debug`). tracing_subscriber::fmt::init(); - - - - let native_options = eframe::NativeOptions::default(); + let native_options = eframe::NativeOptions{ + always_on_top: false, + maximized: true, + decorated: true, + drag_and_drop_support: true, + icon_data: None, + initial_window_pos: None, + initial_window_size: Option::from( + egui::Vec2::new(1000 as f32, 1800 as f32) + ), + min_window_size: None, + max_window_size: None, + resizable: true, + transparent: true, + vsync: true, + multisampling: 0, + depth_buffer: 0, + stencil_buffer: 0, + fullscreen: true, + mouse_passthrough: Default::default(), + hardware_acceleration: eframe::HardwareAcceleration::Preferred, + renderer: eframe::Renderer::Glow, + follow_system_theme: Default::default(), + default_theme:eframe::Theme::Dark, + run_and_return:Default::default(), + event_loop_builder: Default::default(), + shader_version: Default::default(), + centered: true, + }; eframe::run_native( "TypeFast", native_options, diff --git a/src/random/mod.rs b/src/random/mod.rs new file mode 100644 index 0000000..713374c --- /dev/null +++ b/src/random/mod.rs @@ -0,0 +1,30 @@ +use rand::Rng; + + + + + +pub fn random_letters(max: u32) -> String { + let mut rng = rand::thread_rng(); + let mut s: String = "".into(); + for _i in 0..max { + let letter: char = rng.gen_range(b'A'..=b'Z') as char; + let manipulated = big_small_space(letter); + s.push(manipulated) + } + s.trim().to_string() +} + +fn big_small_space(letter: char) -> char { + let mut rng = rand::thread_rng(); + let lr = rng.gen_range(0..3); + match lr { + 0 => letter, + 1 => { + let chars: Vec = letter.to_lowercase().to_string().chars().collect(); + chars[0] + } + 2 => ' ', + _ => letter, + } +} \ No newline at end of file diff --git a/src/settings/mod.rs b/src/settings/mod.rs new file mode 100644 index 0000000..4a8ce56 --- /dev/null +++ b/src/settings/mod.rs @@ -0,0 +1,62 @@ +use egui::Ui; + +#[derive(serde::Deserialize, serde::Serialize)] +#[serde(default)] // if we add new fields, give them default values when deserializing old state +pub struct TFSetting { + pub command: String, + pub settings_open: bool, +} + + + + + + +impl Default for TFSetting { + fn default() -> Self { + Self { + command: Default::default(), + settings_open: false, + } + } +} + +impl TFSetting { + pub fn process_command(&mut self, ui: &mut Ui, ctx: &egui::Context) { + let command = self.command.clone(); + if self.command.contains(";") { + self.command.clear(); + } + + if command.eq("") { + ui.label(&self.command); + } + + if command.eq("open settings;") { + self.settings_open = true + } + + if command.eq("close settings;") { + self.settings_open = false + } + + if self.settings_open { + egui::Window::new("Draft setting").show(ctx, |ui| { + ui.label("Hey"); + if ui.button("close").clicked() { + self.settings_open = false + } + }); + } + } + pub fn command_helpers(&mut self, ui: &mut Ui,){ + ui.spacing_mut().item_spacing.x = 0.5; + if ui.button("open settings").clicked() { + self.command = "open settings;".into(); + } + if ui.button("close settings").clicked() { + self.command = "close settings;".into(); + } + } +} + diff --git a/src/typewriter/mod.rs b/src/typewriter/mod.rs new file mode 100644 index 0000000..dd405ee --- /dev/null +++ b/src/typewriter/mod.rs @@ -0,0 +1,62 @@ +use egui::{Ui, RichText}; + +#[derive(serde::Deserialize, serde::Serialize)] +#[serde(default)] // if we add new fields, give them default values when deserializing old state +pub struct TypeState { + input: String, + challenge: String, +} + +impl Default for TypeState { + fn default() -> Self { + Self { + input: Default::default(), + challenge: Default::default(), + } + } +} + +pub trait Challenge { + fn into_challenge(&self) -> String; +} + + + +impl TypeState { + pub fn render(&mut self, ui: &mut Ui, provider: &(impl Challenge + ?Sized)) { + //win condition + if self.input.eq(&self.challenge) { + self.challenge.clear(); + self.input.clear(); + self.challenge = provider.into_challenge(); + } + + let challenge_text = RichText::new(self.challenge.to_string()).size(90.0); + ui.heading(challenge_text); + + let input_text = RichText::new(self.input.to_string()).size(90.0); + ui.heading(input_text); + + ui.text_edit_multiline(&mut self.input); + + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + ui.label(""); + + + + } +} + +