From f79ab434ff17d07a8ed0cc811afa611b6936864d Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Fri, 2 Oct 2026 17:29:48 +0300 Subject: [PATCH 1/9] config(Cargo.toml): add [profile.release] and agressive optimizations, add clap to dependencies for CLI parsing, and fix cargo-clippy package name warning --- Cargo.toml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 566736a..64e66f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,16 @@ [package] -name = "simple_menu" +name = "simple-menu" version = "0.2.3" edition = "2024" [dependencies] +clap = { version = "4.6.7", features = ["derive"] } macroquad = "0.4" + +[profile.release] +lto = "fat" +panic = "abort" +opt-level = 3 +codegen-units = 1 +strip = true + From 93d4e8f8ebd31084025b82e04c895815faad3a6e Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Fri, 2 Oct 2026 21:57:46 +0300 Subject: [PATCH 2/9] refactor(main): extract menu types and adopt clap CLI Begin modularizing main.rs by offloading core game state, color, and dot definitions to `simple_menu`. Introduce `clap` for command-line argument parsing and clean up inline formatting. This commit lays the groundwork; remaining module integrations and cleanup will follow in subsequent commits. --- Cargo.lock | 181 +++++++++++++++++++++++++++++++++++++++++++++++- Cargo.toml | 2 +- src/lib.rs | 77 +++++++++++++++++++++ src/main.rs | 196 ++++++++++++++++++++++------------------------------ 4 files changed, 340 insertions(+), 116 deletions(-) create mode 100644 src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 22fd879..785ac7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,6 +14,56 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -44,12 +94,58 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "clap" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa8876b300ab35ba921adea3dfd70157a46249b33f95c9084ae5709785478946" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0797fb7aeb1406c84efac526901f7ec3ead2124f946b494e72879d4b54704d" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9c751b79415d4e559e3d1fcf128e09e720eb673a06d26cf6f392d37d75b66e0" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" + [[package]] name = "color_quant" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + [[package]] name = "crc32fast" version = "1.5.0" @@ -117,6 +213,12 @@ dependencies = [ "foldhash", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "image" version = "0.24.9" @@ -130,6 +232,12 @@ dependencies = [ "png", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + [[package]] name = "libc" version = "0.2.177" @@ -211,6 +319,12 @@ dependencies = [ "malloc_buf", ] +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + [[package]] name = "png" version = "0.17.16" @@ -224,12 +338,30 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + [[package]] name = "quad-rand" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a651516ddc9168ebd67b24afd085a718be02f8858fe406591b013d101ce2f40" +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + [[package]] name = "simd-adler32" version = "0.3.7" @@ -237,18 +369,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] -name = "simple_menu" -version = "0.2.3" +name = "simple-menu" +version = "0.2.4" dependencies = [ + "clap", "macroquad", ] +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8593e8e72159ed2257d083c7a454a85cbf854f37a0966d8d483aff8c8a3ebcee" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "ttf-parser" version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" +[[package]] +name = "unicode-ident" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d245f478577f809a851594d02313b640fb437e0bb33866753cff937863096954" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "winapi" version = "0.3.9" @@ -270,3 +432,18 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/Cargo.toml b/Cargo.toml index 64e66f5..fad8de2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "simple-menu" -version = "0.2.3" +version = "0.2.4" edition = "2024" [dependencies] diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5b3f906 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,77 @@ +pub use macroquad::prelude::*; + +/// I mean if you needed a documentation for that enum, +/// the problem is not the developer didn't document it; +/// it's you. +pub enum CurrentState { + Game, + MainMenu, + Settings, +} + +#[derive(Clone, Copy, Debug)] +pub enum BgColor { + Red, + Green, + Blue, + Pink, + Purple, +} + +/// Some boilerplate for changing color A -> B +impl BgColor { + pub fn next(self) -> BgColor { + match self { + BgColor::Red => BgColor::Green, + BgColor::Green => BgColor::Blue, + BgColor::Blue => BgColor::Pink, + BgColor::Pink => BgColor::Purple, + BgColor::Purple => BgColor::Red, + } + } +} + +/// Just a more idiomatic way to convert one data type to another +/// In that case: BgColor -> Color +impl From for Color { + fn from(value: BgColor) -> Self { + match value { + BgColor::Red => RED, + BgColor::Green => GREEN, + BgColor::Blue => BLUE, + BgColor::Pink => PINK, + BgColor::Purple => PURPLE, + } + } +} + +/// simple struct to represent a cute dot +pub struct CuteDot { + pub pos: Vec2, + pub vel: Vec2, + pub radius: f32, + pub color: Color, +} +impl Default for CuteDot { + fn default() -> Self { + Self { + pos: Vec2::ZERO, + vel: vec2(50.0, 50.0), + radius: 5.0, + color: YELLOW, + } + } +} + +impl CuteDot { + pub fn new(pos: Vec2) -> Self { + Self { + pos, + ..Default::default() + } + } + pub fn draw(&self) { + draw_circle(self.pos.x, self.pos.y, self.radius, self.color); + draw_circle_lines(self.pos.x, self.pos.y, self.radius, 2.0, BLACK); + } +} diff --git a/src/main.rs b/src/main.rs index 906b460..d8ee429 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,101 +1,23 @@ -// tried to make comments because people might read this code and not understand it otherwise -// don't worry neither do i fully understand it sometimes -use macroquad::prelude::*; -enum CurrentState { - Game, - MainMenu, - Settings, -} -#[derive(Clone, Copy, Debug)] -enum BgColor { - RED, - GREEN, - BLUE, - PINK, - PURPLE, -} -// okay the following 2? i couldn't explain, like i'm trying to be somewhat professional BUT WHO THOUGHT MAKING COLOR GO FROM A TO B COULD BE THIS HARD FFS -impl BgColor { - fn next(self) -> BgColor { - match self { - BgColor::RED => BgColor::GREEN, - BgColor::GREEN => BgColor::BLUE, - BgColor::BLUE => BgColor::PINK, - BgColor::PINK => BgColor::PURPLE, - BgColor::PURPLE => BgColor::RED, - } - } -} -impl BgColor { - fn to_color(self) -> Color { - match self { - BgColor::RED => RED, - BgColor::GREEN => GREEN, - BgColor::BLUE => BLUE, - BgColor::PINK => PINK, - BgColor::PURPLE => PURPLE, - } - } -} -// simple struct to represent a cute dot -struct CuteDot { - pos: Vec2, - vel: Vec2, - radius: f32, - color: Color, -} -// calling them cute dots bc why not -fn new_cute_dot(pos: Vec2) -> CuteDot { - CuteDot { - pos, - vel: vec2(50.0, 50.0), - radius: 5.0, - color: YELLOW, - } -} -fn update_cute_dot(dot: &mut CuteDot, dt: f32, area_pos: Vec2, area_dim: Vec2) { - dot.pos += dot.vel * dt; -// each of these variables represents the boundaries of the area the dot can move in - let left = area_pos.x + dot.radius; - let right = area_pos.x + area_dim.x - dot.radius; - let top = area_pos.y + dot.radius; - let bottom = area_pos.y + area_dim.y - dot.radius; +//! I tried to document this code so it would be easier to contribue and maintain the game, but I ran into a problem! +//! I barely manage to maintain it! +//! good luck I guess +//! (no budget for actual emoji, deal with that) - // below is simple collision detection with the area boundaries - if dot.pos.x < left { - dot.pos.x = left; - dot.vel.x *= -1.0; - } else if dot.pos.x > right { - dot.pos.x = right; - dot.vel.x *= -1.0; - } - - if dot.pos.y < top { - dot.pos.y = top; - dot.vel.y *= -1.0; - } else if dot.pos.y > bottom { - dot.pos.y = bottom; - dot.vel.y *= -1.0; - } -} +use simple_menu::*; -fn draw_cute_dot(dot: &CuteDot) { - draw_circle(dot.pos.x, dot.pos.y, dot.radius, dot.color); - draw_circle_lines(dot.pos.x, dot.pos.y, dot.radius, 2.0, BLACK); -} fn spawn_dot_in_box(dots: &mut Vec, game_pos: Vec2, game_dim: Vec2) { let radius = 5.0; let x = macroquad::rand::gen_range(game_pos.x + radius, game_pos.x + game_dim.x - radius); let y = macroquad::rand::gen_range(game_pos.y + radius, game_pos.y + game_dim.y - radius); - let mut dot = new_cute_dot(vec2(x, y)); + let mut dot = CuteDot::new(vec2(x, y)); dot.radius = radius; dots.push(dot); } fn text_dimensions(text: &str, font_size: f32) -> Vec2 { let dims = measure_text(text, None, font_size as u16, 1.0); - return vec2(dims.width, dims.height); + vec2(dims.width, dims.height) } // WORK IN PROGRESS FUNCTION, DO NOT USE YET @@ -105,7 +27,7 @@ fn text_dimensions(text: &str, font_size: f32) -> Vec2 { // please ignore the mess that is this function fn game_msg(text: &str, position: Vec2, font_size: f32) { - draw_text(text, position.x, position.y, font_size as f32, WHITE); + draw_text(text, position.x, position.y, font_size, WHITE); } //this function draws an outline around the screen, used for visual effect fn screen_outline(x: f32, y: f32, w: f32, h: f32) { @@ -139,12 +61,12 @@ fn dev_mode_display(dev_mode: bool, mouse: (f32, f32), fps: i32, amount_of_dots: let dev_txt_dim = text_dimensions("developer mode active", 20.0); let cords_txt_dim = text_dimensions(&format!("x: {} | y: {}", mouse.0, mouse.1), 20.0); let fps_txt_dim = text_dimensions(&format!("FPS: {}", fps), 20.0); - let dots_txt_dim = text_dimensions(&format!("dots: {}", amount_of_dots), 20.0); + let dots_txt_dim = text_dimensions(&format!("dots: {amount_of_dots}"), 20.0); let dev_dim = vec2(dev_txt_dim.x, dev_txt_dim.y); let fps_dim = vec2(fps_txt_dim.x, fps_txt_dim.y); let cords_dim = vec2(cords_txt_dim.x, cords_txt_dim.y); - let dots_dim = vec2(dots_txt_dim.x, dots_txt_dim.y); + let dots_dim = vec2(dots_txt_dim.x, dots_txt_dim.y); draw_text( &format!("x: {} | y: {}", mouse.0, mouse.1), @@ -155,7 +77,7 @@ fn dev_mode_display(dev_mode: bool, mouse: (f32, f32), fps: i32, amount_of_dots: ); draw_text( - &format!("developer mode active"), + "developer mode active", screen_width() * 0.85 - dev_dim.x / 2.0, screen_height() * 0.1 - dev_dim.y / 2.0, 20.0, @@ -164,15 +86,15 @@ fn dev_mode_display(dev_mode: bool, mouse: (f32, f32), fps: i32, amount_of_dots: // although the fps is drawn every frame, it only updates every 10 frames in the main loop to reduce performance impact and change frequency // probably a less complex way to do this but it works for now draw_text( - &format!("FPS: {}", fps), + &format!("FPS: {fps}"), screen_width() * 0.85 - fps_dim.x / 2.0, screen_height() * 0.15 - fps_dim.y / 2.0, 20.0, BLACK, ); - -draw_text( - &format!("dots: {}", amount_of_dots), + + draw_text( + &format!("dots: {amount_of_dots}"), screen_width() * 0.85 - dots_dim.x / 2.0, screen_height() * 0.2 - dots_dim.y / 2.0, 20.0, @@ -181,21 +103,32 @@ draw_text( } } -#[macroquad::main("i click button, i happy")] +use clap::Parser; +#[derive(Parser)] +#[command(version)] +struct Cli { + #[arg(skip = env!("CARGO_PKG_VERSION"))] + version: &'static str, + #[arg(long)] + dev_mode: bool, +} + +#[macroquad::main("I click button, I happy")] async fn main() { + let cli = Cli::parse(); + // fps tracking variables - let mut fps: i32 = 60; - let mut i: i32 = 0; - let max: i32 = 10; + let mut fps = 60; + let mut i = 0; + let max = 10; // lots of variables to keep track of the state of the game - let version = "0.2.3"; - let mut dev_mode = false; + let mut dev_mode = cli.dev_mode; let mut state = CurrentState::MainMenu; - let mut current_color: BgColor = BgColor::PURPLE; + let mut current_color = BgColor::Purple; let mut dots: Vec = Vec::new(); - let mut amount_of_dots: i32 = 20; + let mut amount_of_dots = 20; let rec_dim = vec2(500.0, 300.0); let rec_pos = vec2( @@ -207,14 +140,14 @@ async fn main() { let radius = 5.0; let x = macroquad::rand::gen_range(rec_pos.x + radius, rec_pos.x + rec_dim.x - radius); let y = macroquad::rand::gen_range(rec_pos.y + radius, rec_pos.y + rec_dim.y - radius); - let mut dot = new_cute_dot(vec2(x, y)); + let mut dot = CuteDot::new(vec2(x, y)); dot.radius = radius; dots.push(dot); } loop { let dt = get_frame_time(); - clear_background(current_color.to_color()); + clear_background(Color::from(current_color)); // for fps display in dev mode i += 1; @@ -244,7 +177,7 @@ async fn main() { // this match statement handles the different states of the game, note how each state has its own UI and functionality match state { CurrentState::MainMenu => { - let v_text = version; + let v_text = cli.version; let v_text_dims = measure_text(v_text, None, 20, 1.0); let game_text_pos = vec2(10.0, v_text_dims.height * 3.0); @@ -272,21 +205,34 @@ async fn main() { draw_rectangle(game_pos.x, game_pos.y, game_dim.x, game_dim.y, LIGHTGRAY); game_msg(game_text, game_text_pos, 40.0); -// update and draw each cute dot + // update and draw each cute dot let inside_game_pos = game_pos - vec2(outline_size / 2.0, outline_size / 2.0); for dot in &mut dots { update_cute_dot(dot, dt, inside_game_pos, game_dim); } for dot in &dots { - draw_cute_dot(dot); + dot.draw(); } // draw the game area outline after drawing the dots to ensure it's on top - draw_rectangle_lines(game_pos.x, game_pos.y, game_dim.x, game_dim.y, outline_size, BLACK); - if button(screen_width() / 4.0, screen_height() - 100.0, btn_size.x, btn_size.y, "dots +") { - amount_of_dots += 1; - spawn_dot_in_box(&mut dots, inside_game_pos, game_dim); - println!("dots increased to {}", amount_of_dots); + draw_rectangle_lines( + game_pos.x, + game_pos.y, + game_dim.x, + game_dim.y, + outline_size, + BLACK, + ); + if button( + screen_width() / 4.0, + screen_height() - 100.0, + btn_size.x, + btn_size.y, + "dots +", + ) { + amount_of_dots += 1; + spawn_dot_in_box(&mut dots, inside_game_pos, game_dim); + println!("dots increased to {}", amount_of_dots); } if button(10.0, 10.0, btn_size.x, btn_size.y, "Settings") { state = CurrentState::Settings; @@ -330,5 +276,29 @@ async fn main() { next_frame().await; } } -// hey we made it to the end! congrats! -// hope you found what you were looking for :D + +fn update_cute_dot(dot: &mut CuteDot, dt: f32, area_pos: Vec2, area_dim: Vec2) { + dot.pos += dot.vel * dt; + // each of these variables represents the boundaries of the area the dot can move in + let left = area_pos.x + dot.radius; + let right = area_pos.x + area_dim.x - dot.radius; + let top = area_pos.y + dot.radius; + let bottom = area_pos.y + area_dim.y - dot.radius; + + // below is simple collision detection with the area boundaries + if dot.pos.x < left { + dot.pos.x = left; + dot.vel.x *= -1.0; + } else if dot.pos.x > right { + dot.pos.x = right; + dot.vel.x *= -1.0; + } + + if dot.pos.y < top { + dot.pos.y = top; + dot.vel.y *= -1.0; + } else if dot.pos.y > bottom { + dot.pos.y = bottom; + dot.vel.y *= -1.0; + } +} From 440f01e19d8e3d96c487e948f00e0df69f716164 Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Fri, 2 Oct 2026 22:17:25 +0300 Subject: [PATCH 3/9] push LICENSE file (MIT license) --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..36930cd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Zot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 1661ac66ecde58e0a1df24ccfd55627685571708 Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Fri, 2 Oct 2026 22:18:59 +0300 Subject: [PATCH 4/9] docs(readme): update readme cause previous one (CodeWithCompound's) was an unedited template (Mine is no less than PEAK). --- README.md | 93 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 74 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index d77e395..67e6c13 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,90 @@ -# Simple Menu -![screenshot](preview_menu.png) -[![License](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT) +# simple-menu -a pretty simple menu you can navigate, it includes a little partical simulator, settings and a starting screen! +![preview](preview_menu.png) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![Rust Edition](https://img.shields.io/badge/Rust-2024%20Edition-orange.svg)](https://www.rust-lang.org/) + +> "I click button, I happy." + +A ridiculously over-engineered, lightweight menu playground and bouncing dot simulator written in Rust with Macroquad. + +It kind of looks like 2001 in a nutshell, but now with clap CLI arguments and release profile optimizations that treat 20 bouncing circles like a AAA physics engine. --- -## Tech Stack +## What is this? -- Language: Rust -- Main libs/frameworks: Macroquad +Calling it a "game" is definitely a stretch, but it has three screens and buttons you can click: + +- **Main Menu**: Shows the version and a big START button. When you click it, it prints `game started!` to stdout and throws you into the action. +- **Game Prototype**: A gray box with "Cute Dots" bouncing around inside. If 20 dots aren't enough, smash the **dots +** button to spawn more. +- **Settings**: A placeholder paradise where you can cycle the background through Purple, Red, Green, Blue, and Pink, or toggle Developer Mode. --- -## Features +## Over-Engineered Improvements -![done](https://img.shields.io/badge/status-done-brightgreen) -- 3 menues you can navigate -- each menu has it`s core features in place ( Start screen with start button, Game screen with partical simulator and the Settings to tweak stuff) +Recently forked and given far more engineering discipline than a dot simulator probably deserves: -![wip](https://img.shields.io/badge/status-WIP-blue) -- making it pretty... it kind of looks like 2001 in a nut shell -- giving more settings & options for both the Settings & Game tab -- a game title (i keep forgetting that) +- **Clap CLI integration**: You can now pass `--dev-mode` directly from your terminal to launch straight into the developer HUD. +- **Aggressive compiler optimizations**: Configured `Cargo.toml` with `opt-level = 3`, `lto = "fat"`, `codegen-units = 1`, `panic = "abort"`, and `strip = true`. Because 20 bouncing dots deserve bare-metal performance. +- **Modular crate split**: Decoupled `src/lib.rs` from `src/main.rs`. Now `CurrentState`, `BgColor`, and `CuteDot` live in their own library crate. +- **Developer HUD**: Displays live mouse coordinates `(x, y)`, total dot count, and an FPS counter that updates every 10 frames to avoid burning out your screen. --- -### Installation +## Running It + +Make sure you have Rust installed. ```bash -# clone the repo -git clone https://github.com/USERNAME/REPO_NAME.git -cd REPO_NAME +# Clone the repository +git clone https://github.com/Jacob-Dayan/simple-menu.git +cd simple-menu + +# Run it +cargo run +``` + +### CLI Options + +Thanks to `clap`, there are actual command-line flags: + +```bash +# Launch directly with the developer overlay active +cargo run -- --dev-mode + +# Print version +cargo run -- --version + +# Print help +cargo run -- --help +``` + +--- + +## How to "Play" + +| Screen | Buttons | What happens | +| :--- | :--- | :--- | +| Main Menu | **START** | Takes you to the simulation. | +| Game | **dots +** | Spawns another dot. | +| Game | **Settings** | Opens the settings. | +| Settings | **Choose color** | Cycles background color. | +| Settings | **Dev: true/false** | Toggles the HUD showing FPS and mouse coordinates. | +| Settings | **Back to game** | Takes you back to the bouncing dots. | + +--- + +## Tech Stack + +- **Rust** (2024 edition) +- **Macroquad** (for rendering windows, rectangles, and circles) +- **Clap** (for parsing two CLI flags with maximum type safety) + +--- + +## License + +MIT. Do whatever you want with it. From df7e4f8d759b0e137c2c0df17eebabca44b5ef81 Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Fri, 2 Oct 2026 23:19:26 +0300 Subject: [PATCH 5/9] git: remove all sh!t from .gitignore and flex for using Helix --- .gitignore | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.gitignore b/.gitignore index eca2202..9092d51 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,2 @@ -# Rust build junk -target/ -debug/ -flycheck* -*.rs.bk -*.pdb -*.exe -*.dll - -# Editor junk -.vscode/ -.idea/ +/target +/.helix # I use Helix btw From c6e14a6c21fa883c60813b217016259d0f7d0075 Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Sat, 3 Oct 2026 13:22:08 +0300 Subject: [PATCH 6/9] refactor: turn chaos into architecture and pretend it's a real engine Swapped spaghetti boundary checks for proper Rect methods, taught dots how to update themselves, and cleaned up the UI logic. Still completely unnecessary for bouncing 20 circles, but at least the code looks like someone with a plan wrote it. --- src/lib.rs | 50 +++++++ src/main.rs | 319 ++++++++++++++++++++------------------------ target/CACHEDIR.TAG | 3 - 3 files changed, 194 insertions(+), 178 deletions(-) delete mode 100644 target/CACHEDIR.TAG diff --git a/src/lib.rs b/src/lib.rs index 5b3f906..82841d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -46,12 +46,14 @@ impl From for Color { } /// simple struct to represent a cute dot +#[derive(Clone, Debug)] pub struct CuteDot { pub pos: Vec2, pub vel: Vec2, pub radius: f32, pub color: Color, } + impl Default for CuteDot { fn default() -> Self { Self { @@ -70,8 +72,56 @@ impl CuteDot { ..Default::default() } } + + /// Spawns a dot randomly inside the given boundary box. + /// Because doing math to place dots by hand was getting old. + pub fn spawn_in_bounds(bounds: Rect) -> Self { + let radius = 5.0; + let x = macroquad::rand::gen_range(bounds.left() + radius, bounds.right() - radius); + let y = macroquad::rand::gen_range(bounds.top() + radius, bounds.bottom() - radius); + Self { + pos: vec2(x, y), + radius, + ..Default::default() + } + } + + /// Updates the dot's position and bounces off the boundary walls. + /// If you find a bug here, no you didn't. + pub fn update(&mut self, dt: f32, bounds: Rect) { + self.pos += self.vel * dt; + + // each of these variables represents the boundaries of the area the dot can move in + let left = bounds.left() + self.radius; + let right = bounds.right() - self.radius; + let top = bounds.top() + self.radius; + let bottom = bounds.bottom() - self.radius; + + // below is simple collision detection with the area boundaries + if self.pos.x < left { + self.pos.x = left; + self.vel.x *= -1.0; + } else if self.pos.x > right { + self.pos.x = right; + self.vel.x *= -1.0; + } + + if self.pos.y < top { + self.pos.y = top; + self.vel.y *= -1.0; + } else if self.pos.y > bottom { + self.pos.y = bottom; + self.vel.y *= -1.0; + } + } + pub fn draw(&self) { draw_circle(self.pos.x, self.pos.y, self.radius, self.color); draw_circle_lines(self.pos.x, self.pos.y, self.radius, 2.0, BLACK); } } + +/// Helper to spawn a dot and push it directly into a collection +pub fn spawn_dot_in_box(dots: &mut Vec, bounds: Rect) { + dots.push(CuteDot::spawn_in_bounds(bounds)); +} diff --git a/src/main.rs b/src/main.rs index d8ee429..edb4987 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,105 +5,115 @@ use simple_menu::*; -fn spawn_dot_in_box(dots: &mut Vec, game_pos: Vec2, game_dim: Vec2) { - let radius = 5.0; - let x = macroquad::rand::gen_range(game_pos.x + radius, game_pos.x + game_dim.x - radius); - let y = macroquad::rand::gen_range(game_pos.y + radius, game_pos.y + game_dim.y - radius); - - let mut dot = CuteDot::new(vec2(x, y)); - dot.radius = radius; - dots.push(dot); +const DEFAULT_DOTS_COUNT: usize = 20; +const GAME_AREA_SIZE: Vec2 = vec2(500.0, 300.0); +const BTN_SIZE: Vec2 = vec2(200.0, 60.0); +const COLOR_BTN_SIZE: Vec2 = vec2(200.0, 50.0); +const SCREEN_OUTLINE_THICKNESS: f32 = 10.0; +const GAME_OUTLINE_THICKNESS: f32 = 10.0; +const BTN_OUTLINE_THICKNESS: f32 = 4.0; +const FPS_UPDATE_INTERVAL: i32 = 10; + +fn game_bounds() -> Rect { + Rect::new( + (screen_width() - GAME_AREA_SIZE.x) / 2.0, + (screen_height() - GAME_AREA_SIZE.y) / 2.0, + GAME_AREA_SIZE.x, + GAME_AREA_SIZE.y, + ) +} + +fn centered_rect(center_x: f32, y: f32, size: Vec2) -> Rect { + Rect::new(center_x - size.x / 2.0, y, size.x, size.y) } -fn text_dimensions(text: &str, font_size: f32) -> Vec2 { - let dims = measure_text(text, None, font_size as u16, 1.0); - vec2(dims.width, dims.height) +// FORMERLY "WORK IN PROGRESS FUNCTION, DO NOT USE YET" +// The existential crisis has been resolved: it now returns TextDimensions with actual width +// and height fields instead of stuffing dimensions into a Vec2 like a barbarian. +fn text_dimensions(text: &str, font_size: f32) -> TextDimensions { + measure_text(text, None, font_size as u16, 1.0) } -// WORK IN PROGRESS FUNCTION, DO NOT USE YET -// actually never mind it's done now -// i dont really like how it returns a vec2 when it could return a struct with width and height fields but oh well -// i might change that later -// please ignore the mess that is this function +fn centered_text_pos(text: &str, center_x: f32, center_y: f32, font_size: f32) -> Vec2 { + let dims = text_dimensions(text, font_size); + vec2(center_x - dims.width / 2.0, center_y - dims.height / 2.0) +} fn game_msg(text: &str, position: Vec2, font_size: f32) { draw_text(text, position.x, position.y, font_size, WHITE); } + //this function draws an outline around the screen, used for visual effect -fn screen_outline(x: f32, y: f32, w: f32, h: f32) { - draw_rectangle_lines(x, y, w, h, 10.0, BLACK); +fn screen_outline() { + draw_rectangle_lines( + 0.0, + 0.0, + screen_width(), + screen_height(), + SCREEN_OUTLINE_THICKNESS, + BLACK, + ); } + // simple button function, returns true if clicked yes i know it's basic but hey it works -fn button(x: f32, y: f32, w: f32, h: f32, label: &str) -> bool { +fn button(rect: Rect, label: &str) -> bool { let (mx, my) = mouse_position(); - let inside_x = mx >= x && mx <= x + w; - let inside_y = my >= y && my <= y + h; - let hovered = inside_x && inside_y; + let hovered = rect.contains(vec2(mx, my)); - if hovered { - draw_rectangle(x, y, w, h, LIGHTGRAY); - draw_text(label, x + 10.0, y + h * 0.6, 31.0, BLACK); + let (bg, font_size) = if hovered { + (LIGHTGRAY, 31.0) } else { - draw_rectangle(x, y, w, h, GRAY); - draw_text(label, x + 10.0, y + h * 0.6, 30.0, BLACK); - } - draw_rectangle_lines(x, y, w, h, 4.0, BLACK); + (GRAY, 30.0) + }; + + draw_rectangle(rect.x, rect.y, rect.w, rect.h, bg); + draw_text( + label, + rect.x + 10.0, + rect.y + rect.h * 0.6, + font_size, + BLACK, + ); + draw_rectangle_lines(rect.x, rect.y, rect.w, rect.h, BTN_OUTLINE_THICKNESS, BLACK); hovered && is_mouse_button_pressed(MouseButton::Left) } -fn dev_mode_display(dev_mode: bool, mouse: (f32, f32), fps: i32, amount_of_dots: i32) { + +fn dev_mode_display(dev_mode: bool, mouse: (f32, f32), fps: i32, amount_of_dots: usize) { // displays mouse coordinates and a dev mode message when dev mode is active // the function is called every frame, but only draws when dev_mode is true // which is efficient enough for this simple use case // but i should probably add some throttling or optimization if this were to be used in a more complex application oh well if dev_mode { - let dev_txt_dim = text_dimensions("developer mode active", 20.0); - let cords_txt_dim = text_dimensions(&format!("x: {} | y: {}", mouse.0, mouse.1), 20.0); - let fps_txt_dim = text_dimensions(&format!("FPS: {}", fps), 20.0); - let dots_txt_dim = text_dimensions(&format!("dots: {amount_of_dots}"), 20.0); - - let dev_dim = vec2(dev_txt_dim.x, dev_txt_dim.y); - let fps_dim = vec2(fps_txt_dim.x, fps_txt_dim.y); - let cords_dim = vec2(cords_txt_dim.x, cords_txt_dim.y); - let dots_dim = vec2(dots_txt_dim.x, dots_txt_dim.y); - - draw_text( - &format!("x: {} | y: {}", mouse.0, mouse.1), - screen_width() * 0.85 - cords_dim.x / 2.0, - screen_height() * 0.05 - cords_dim.y / 2.0, - 20.0, - BLACK, - ); - - draw_text( - "developer mode active", - screen_width() * 0.85 - dev_dim.x / 2.0, - screen_height() * 0.1 - dev_dim.y / 2.0, - 20.0, - BLACK, - ); - // although the fps is drawn every frame, it only updates every 10 frames in the main loop to reduce performance impact and change frequency - // probably a less complex way to do this but it works for now - draw_text( - &format!("FPS: {fps}"), - screen_width() * 0.85 - fps_dim.x / 2.0, - screen_height() * 0.15 - fps_dim.y / 2.0, - 20.0, - BLACK, - ); - - draw_text( - &format!("dots: {amount_of_dots}"), - screen_width() * 0.85 - dots_dim.x / 2.0, - screen_height() * 0.2 - dots_dim.y / 2.0, - 20.0, - BLACK, - ); + let entries = [ + format!("x: {} | y: {}", mouse.0, mouse.1), + "developer mode active".to_string(), + format!("FPS: {fps}"), + format!("dots: {amount_of_dots}"), + ]; + + let font_size = 20.0; + let base_x = screen_width() * 0.85; + + for (idx, line) in entries.iter().enumerate() { + let dims = text_dimensions(line, font_size); + // although the fps is drawn every frame, it only updates every 10 frames in the main loop to reduce performance impact and change frequency + // probably a less complex way to do this but it works for now + let y_pos = screen_height() * (0.05 * (idx + 1) as f32); + draw_text( + line, + base_x - dims.width / 2.0, + y_pos - dims.height / 2.0, + font_size, + BLACK, + ); + } } } use clap::Parser; + #[derive(Parser)] #[command(version)] struct Cli { @@ -111,6 +121,8 @@ struct Cli { version: &'static str, #[arg(long)] dev_mode: bool, + #[arg(long)] + dots_count: Option, } #[macroquad::main("I click button, I happy")] @@ -120,29 +132,18 @@ async fn main() { // fps tracking variables let mut fps = 60; let mut i = 0; - let max = 10; // lots of variables to keep track of the state of the game let mut dev_mode = cli.dev_mode; let mut state = CurrentState::MainMenu; let mut current_color = BgColor::Purple; - let mut dots: Vec = Vec::new(); - let mut amount_of_dots = 20; - - let rec_dim = vec2(500.0, 300.0); - let rec_pos = vec2( - screen_width() / 2.0 - rec_dim.x / 2.0, - screen_height() / 2.0 - rec_dim.y / 2.0, - ); + let mut amount_of_dots = cli.dots_count.unwrap_or(DEFAULT_DOTS_COUNT); + let mut dots: Vec = Vec::with_capacity(amount_of_dots); + let initial_bounds = game_bounds(); for _ in 0..amount_of_dots { - let radius = 5.0; - let x = macroquad::rand::gen_range(rec_pos.x + radius, rec_pos.x + rec_dim.x - radius); - let y = macroquad::rand::gen_range(rec_pos.y + radius, rec_pos.y + rec_dim.y - radius); - let mut dot = CuteDot::new(vec2(x, y)); - dot.radius = radius; - dots.push(dot); + spawn_dot_in_box(&mut dots, initial_bounds); } loop { @@ -151,39 +152,35 @@ async fn main() { // for fps display in dev mode i += 1; - if i >= max { + if i >= FPS_UPDATE_INTERVAL { i = 0; fps = get_fps(); } - // get screen dimensions and button dimensions - let screen = vec2(screen_width(), screen_height()); - let btn_size = vec2(200.0, 60.0); - let outline_size = vec2(screen_width(), screen_height()); - let mouse: (f32, f32) = mouse_position(); - // center position: screen/2 - size/2 - // this is used to center the button on the screen - let btn_pos = vec2( - screen.x / 2.0 - btn_size.x / 2.0, - screen.y / 2.0 - btn_size.y / 2.0, - ); - let outline_pos = vec2( - screen.x / 2.0 - outline_size.x / 2.0, - screen.y / 2.0 - outline_size.y / 2.0, - ); + // draw the outline around the screen - screen_outline(outline_pos.x, outline_pos.y, screen.x, screen.y); + screen_outline(); + // display dev mode info if active - dev_mode_display(dev_mode, mouse, fps, amount_of_dots); + dev_mode_display(dev_mode, mouse_position(), fps, amount_of_dots); + // this match statement handles the different states of the game, note how each state has its own UI and functionality match state { CurrentState::MainMenu => { let v_text = cli.version; - let v_text_dims = measure_text(v_text, None, 20, 1.0); + let v_text_dims = text_dimensions(v_text, 20.0); let game_text_pos = vec2(10.0, v_text_dims.height * 3.0); game_msg(v_text, game_text_pos, 40.0); - if button(btn_pos.x, btn_pos.y, btn_size.x, btn_size.y, "START") { + // center position: screen/2 - size/2 + // this is used to center the button on the screen + let btn_rect = centered_rect( + screen_width() / 2.0, + (screen_height() - BTN_SIZE.y) / 2.0, + BTN_SIZE, + ); + + if button(btn_rect, "START") { println!("game started!"); state = CurrentState::Game; } @@ -191,50 +188,50 @@ async fn main() { CurrentState::Game => { // actually calling it a game is a bit of a stretch let game_text = "game prototype:"; - let game_text_dims = measure_text(game_text, None, 40, 1.0); - let game_text_pos = vec2( - screen_width() / 2.0 - game_text_dims.width / 2.0, - screen_height() / 4.0 - game_text_dims.height / 2.0, - ); - let game_dim = vec2(500.0, 300.0); - let game_pos = vec2( - screen_width() / 2.0 - game_dim.x / 2.0, - screen_height() / 2.0 - game_dim.y / 2.0, - ); - let outline_size = 10.0; + let game_bounds = game_bounds(); - draw_rectangle(game_pos.x, game_pos.y, game_dim.x, game_dim.y, LIGHTGRAY); + draw_rectangle( + game_bounds.x, + game_bounds.y, + game_bounds.w, + game_bounds.h, + LIGHTGRAY, + ); + let game_text_pos = + centered_text_pos(game_text, screen_width() / 2.0, screen_height() / 4.0, 40.0); game_msg(game_text, game_text_pos, 40.0); - // update and draw each cute dot - let inside_game_pos = game_pos - vec2(outline_size / 2.0, outline_size / 2.0); + // update and draw each cute dot for dot in &mut dots { - update_cute_dot(dot, dt, inside_game_pos, game_dim); - } - for dot in &dots { + dot.update(dt, game_bounds); dot.draw(); } + // draw the game area outline after drawing the dots to ensure it's on top draw_rectangle_lines( - game_pos.x, - game_pos.y, - game_dim.x, - game_dim.y, - outline_size, + game_bounds.x, + game_bounds.y, + game_bounds.w, + game_bounds.h, + GAME_OUTLINE_THICKNESS, BLACK, ); + if button( - screen_width() / 4.0, - screen_height() - 100.0, - btn_size.x, - btn_size.y, + Rect::new( + screen_width() / 4.0, + screen_height() - 100.0, + BTN_SIZE.x, + BTN_SIZE.y, + ), "dots +", ) { amount_of_dots += 1; - spawn_dot_in_box(&mut dots, inside_game_pos, game_dim); + spawn_dot_in_box(&mut dots, game_bounds); println!("dots increased to {}", amount_of_dots); } - if button(10.0, 10.0, btn_size.x, btn_size.y, "Settings") { + + if button(Rect::new(10.0, 10.0, BTN_SIZE.x, BTN_SIZE.y), "Settings") { state = CurrentState::Settings; } } @@ -242,33 +239,31 @@ async fn main() { // i might add more settings later but for now this is fine // also the settings text at the bottom is just a placeholder for now let settings_text = "change your settings as you desire! (SETTINGS SOON!!)"; - let settings_text_dims = measure_text(settings_text, None, 30, 1.0); - let settings_text_pos: Vec2 = vec2( - screen_width() / 2.0 - settings_text_dims.width / 2.0, - screen_height() - settings_text_dims.height / 2.0, + let settings_dims = text_dimensions(settings_text, 30.0); + let settings_text_pos = vec2( + (screen_width() - settings_dims.width) / 2.0, + screen_height() - settings_dims.height / 2.0, ); game_msg(settings_text, settings_text_pos, 30.0); - let color_btn_size = vec2(200.0, 50.0); + if button( - screen_width() / 2.0 - btn_size.x / 2.0, - 125.0, - color_btn_size.x, - color_btn_size.y, + centered_rect(screen_width() / 2.0, 125.0, COLOR_BTN_SIZE), &format!("Dev: {}", dev_mode), ) { dev_mode = !dev_mode; } + if button( - screen_width() / 2.0 - btn_size.x / 2.0, - 50.0, - color_btn_size.x, - color_btn_size.y, + centered_rect(screen_width() / 2.0, 50.0, COLOR_BTN_SIZE), "Choose color", ) { current_color = current_color.next(); } - if button(10.0, 10.0, btn_size.x, btn_size.y, "Back to game") { + if button( + Rect::new(10.0, 10.0, BTN_SIZE.x, BTN_SIZE.y), + "Back to game", + ) { state = CurrentState::Game; } } @@ -276,29 +271,3 @@ async fn main() { next_frame().await; } } - -fn update_cute_dot(dot: &mut CuteDot, dt: f32, area_pos: Vec2, area_dim: Vec2) { - dot.pos += dot.vel * dt; - // each of these variables represents the boundaries of the area the dot can move in - let left = area_pos.x + dot.radius; - let right = area_pos.x + area_dim.x - dot.radius; - let top = area_pos.y + dot.radius; - let bottom = area_pos.y + area_dim.y - dot.radius; - - // below is simple collision detection with the area boundaries - if dot.pos.x < left { - dot.pos.x = left; - dot.vel.x *= -1.0; - } else if dot.pos.x > right { - dot.pos.x = right; - dot.vel.x *= -1.0; - } - - if dot.pos.y < top { - dot.pos.y = top; - dot.vel.y *= -1.0; - } else if dot.pos.y > bottom { - dot.pos.y = bottom; - dot.vel.y *= -1.0; - } -} diff --git a/target/CACHEDIR.TAG b/target/CACHEDIR.TAG deleted file mode 100644 index 20d7c31..0000000 --- a/target/CACHEDIR.TAG +++ /dev/null @@ -1,3 +0,0 @@ -Signature: 8a477f597d28d172789f06886806bc55 -# This file is a cache directory tag created by cargo. -# For information about cache directory tags see https://bford.info/cachedir/ From a563a2bdf11c87d8906aa51eef327b65758551db Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Sat, 3 Oct 2026 13:33:19 +0300 Subject: [PATCH 7/9] refactor(ui): improve dev mode label and simplify format string --- src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index edb4987..914b3af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -228,7 +228,7 @@ async fn main() { ) { amount_of_dots += 1; spawn_dot_in_box(&mut dots, game_bounds); - println!("dots increased to {}", amount_of_dots); + println!("dots increased to {amount_of_dots}"); } if button(Rect::new(10.0, 10.0, BTN_SIZE.x, BTN_SIZE.y), "Settings") { @@ -248,7 +248,7 @@ async fn main() { if button( centered_rect(screen_width() / 2.0, 125.0, COLOR_BTN_SIZE), - &format!("Dev: {}", dev_mode), + &format!("Dev: {}", if dev_mode { "activated" } else { "disabled" }), ) { dev_mode = !dev_mode; } From 0edc8c9456b814b9bc581256038200179803de86 Mon Sep 17 00:00:00 2001 From: Jacob Dayan Date: Sat, 3 Oct 2026 14:38:11 +0300 Subject: [PATCH 8/9] docs: update preview_menu image --- preview_menu.png | Bin 13681 -> 7982 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/preview_menu.png b/preview_menu.png index dc0ceb990c3c2a59977f0d9876be8af46c7771c5..c0d73568448b44ef0fed97fd95c24d78f42f8f5b 100644 GIT binary patch literal 7982 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 Date: Sat, 3 Oct 2026 22:15:45 +0300 Subject: [PATCH 9/9] refactor: simplify dev_mode_display and rename dots counter --- src/main.rs | 68 +++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/main.rs b/src/main.rs index 914b3af..c71a24e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -55,7 +55,9 @@ fn screen_outline() { ); } -// simple button function, returns true if clicked yes i know it's basic but hey it works +/// A function that checks if pressed a button, +/// or if the mouse is currently hoverring the button -> +/// The button becomes light gray (until unhovered) fn button(rect: Rect, label: &str) -> bool { let (mx, my) = mouse_position(); let hovered = rect.contains(vec2(mx, my)); @@ -79,36 +81,34 @@ fn button(rect: Rect, label: &str) -> bool { hovered && is_mouse_button_pressed(MouseButton::Left) } -fn dev_mode_display(dev_mode: bool, mouse: (f32, f32), fps: i32, amount_of_dots: usize) { +fn dev_mode_display(mouse: (f32, f32), fps: i32, dots_count: usize) { // displays mouse coordinates and a dev mode message when dev mode is active // the function is called every frame, but only draws when dev_mode is true // which is efficient enough for this simple use case // but i should probably add some throttling or optimization if this were to be used in a more complex application oh well - if dev_mode { - let entries = [ - format!("x: {} | y: {}", mouse.0, mouse.1), - "developer mode active".to_string(), - format!("FPS: {fps}"), - format!("dots: {amount_of_dots}"), - ]; - - let font_size = 20.0; - let base_x = screen_width() * 0.85; - - for (idx, line) in entries.iter().enumerate() { - let dims = text_dimensions(line, font_size); - // although the fps is drawn every frame, it only updates every 10 frames in the main loop to reduce performance impact and change frequency - // probably a less complex way to do this but it works for now - let y_pos = screen_height() * (0.05 * (idx + 1) as f32); - draw_text( - line, - base_x - dims.width / 2.0, - y_pos - dims.height / 2.0, - font_size, - BLACK, - ); - } + let entries = [ + format!("x: {} | y: {}", mouse.0, mouse.1), + "developer mode active".to_string(), + format!("FPS: {fps}"), + format!("dots: {dots_count}"), + ]; + + let font_size = 20.0; + let base_x = screen_width() * 0.85; + + for (idx, line) in entries.iter().enumerate() { + let dims = text_dimensions(line, font_size); + // although the fps is drawn every frame, it only updates every 10 frames in the main loop to reduce performance impact and change frequency + // probably a less complex way to do this but it works for now + let y_pos = screen_height() * (0.05 * (idx + 1) as f32); + draw_text( + line, + base_x - dims.width / 2.0, + y_pos - dims.height / 2.0, + font_size, + BLACK, + ); } } @@ -138,11 +138,11 @@ async fn main() { let mut state = CurrentState::MainMenu; let mut current_color = BgColor::Purple; - let mut amount_of_dots = cli.dots_count.unwrap_or(DEFAULT_DOTS_COUNT); - let mut dots: Vec = Vec::with_capacity(amount_of_dots); + let mut dots_count = cli.dots_count.unwrap_or(DEFAULT_DOTS_COUNT); + let mut dots: Vec = Vec::with_capacity(dots_count); let initial_bounds = game_bounds(); - for _ in 0..amount_of_dots { + for _ in 0..dots_count { spawn_dot_in_box(&mut dots, initial_bounds); } @@ -161,7 +161,9 @@ async fn main() { screen_outline(); // display dev mode info if active - dev_mode_display(dev_mode, mouse_position(), fps, amount_of_dots); + if dev_mode { + dev_mode_display(mouse_position(), fps, dots_count); + } // this match statement handles the different states of the game, note how each state has its own UI and functionality match state { @@ -226,9 +228,9 @@ async fn main() { ), "dots +", ) { - amount_of_dots += 1; + dots_count += 1; spawn_dot_in_box(&mut dots, game_bounds); - println!("dots increased to {amount_of_dots}"); + println!("dots increased to {dots_count}"); } if button(Rect::new(10.0, 10.0, BTN_SIZE.x, BTN_SIZE.y), "Settings") { @@ -238,7 +240,7 @@ async fn main() { CurrentState::Settings => { // i might add more settings later but for now this is fine // also the settings text at the bottom is just a placeholder for now - let settings_text = "change your settings as you desire! (SETTINGS SOON!!)"; + let settings_text = "change your settings as you desire!"; let settings_dims = text_dimensions(settings_text, 30.0); let settings_text_pos = vec2( (screen_width() - settings_dims.width) / 2.0,