diff --git a/src/app.rs b/src/app.rs index 47ee048..37957f9 100644 --- a/src/app.rs +++ b/src/app.rs @@ -72,6 +72,11 @@ impl eframe::App for TemplateApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { // Put your widgets into a `SidePanel`, `TopBottomPanel`, `CentralPanel`, `Window` or `Area`. // For inspiration and more examples, go to https://emilk.github.io/egui + + // let mut style = (*ctx.style()).clone(); + // style.visuals.override_text_color = Some(Color32::from_rgb(183, 65, 14)); + // ctx.set_style(style); + if self.first_frame { ctx.set_visuals(egui::Visuals::light()); self.first_frame = false; diff --git a/src/central_panel.rs b/src/central_panel.rs index b886e6a..97e3c73 100644 --- a/src/central_panel.rs +++ b/src/central_panel.rs @@ -1,4 +1,5 @@ use eframe::egui; +use egui::RichText; fn increment(counter: &mut i32) { *counter += 1; @@ -26,13 +27,21 @@ pub fn central_panel_ui( show_area3: &mut bool, ) { egui::CentralPanel::default().show(ctx, |ui| { - ui.heading("rust_eframe_egui project, a learning project following along and building with the Rust book. Find the Rust Book Below!"); + ui.horizontal(|ui|{ + ui.add( + egui::Image::new(egui::include_image!("../assets/Rust_logo_animated1.gif")) + .fit_to_exact_size(egui::vec2(50.0, 50.0)) + .corner_radius(5), + ); + ui.heading(RichText::new("rust_eframe_egui project, a learning project following along and building with the Rust book. Find the Rust Book Below!") + .color(egui::Color32::from_rgb(183, 65, 14)) + .size(18.0) + ); + }); - ui.hyperlink_to("The Rust Programming Language book", "https://doc.rust-lang.org/stable/book"); + let mut theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style()); - egui::Frame::new().fill(egui::Color32::RED).show(ui, |ui| { - ui.label("Label with red background"); - }); + ui.hyperlink_to("The Rust Programming Language book", "https://doc.rust-lang.org/stable/book"); ui.horizontal(|ui| { ui.label("Write something: "); @@ -52,8 +61,6 @@ pub fn central_panel_ui( ui.separator(); - - ui.horizontal(|ui| { ui.hyperlink_to("eframe source code.", "https://github.com/emilk/eframe_template"); @@ -66,6 +73,17 @@ pub fn central_panel_ui( if ui.button("Toggle Area 3").clicked() { *show_area3 = !*show_area3; } + + ui.label("Language:"); + ui.add_enabled_ui(false, |ui|{ + ui.text_edit_singleline(language); + }); + ui.collapsing("Theme", |ui| { + ui.group(|ui| { + theme.ui(ui); + theme.clone().store_in_memory(ui.ctx()); + }); + }); }); if *show_area1 { @@ -77,25 +95,6 @@ pub fn central_panel_ui( .corner_radius(5), ); - ui.horizontal(|ui| { - ui.label("Language:"); - ui.text_edit_singleline(language); - }); - ui.horizontal_wrapped(|ui| { - ui.spacing_mut().item_spacing.x = 0.0; - ui.label("Syntax highlighting powered by "); - ui.hyperlink_to("syntect", "https://github.com/trishume/syntect"); - ui.label("."); - - }); - - let mut theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style()); - ui.collapsing("Theme", |ui| { - ui.group(|ui| { - theme.ui(ui); - theme.clone().store_in_memory(ui.ctx()); - }); - }); let mut layouter = |ui: &egui::Ui, text: &str, wrap_width: f32| { let mut layout_job = egui_extras::syntax_highlighting::highlight( ui.ctx(), @@ -132,6 +131,12 @@ fn main() { .desired_width(f32::INFINITY) .layouter(&mut layouter), ); + ui.horizontal_wrapped(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.label("Syntax highlighting powered by "); + ui.hyperlink_to("syntect", "https://github.com/trishume/syntect"); + ui.label("."); + }); }); } @@ -192,13 +197,16 @@ fn main() { .fit_to_exact_size(egui::vec2(50.0, 50.0)) .corner_radius(5), ); - ui.label("Cool Floating Rustacean in an egui::Area. Areas are dragable, click the center and hold and drag area around to desired position. egui::Area itself does not directly control its size β€” it wraps content and takes the size of whatever UI elements are inside it."); - ui.add( - egui::Image::new(egui::include_image!("../assets/Rust_logo_animated1.gif")) - .fit_to_exact_size(egui::vec2(200.0, 200.0)) - .corner_radius(5), - ); + ui.label("Cool Floating Rustacean in an egui::Area. Areas are dragable, Drag and Reposition an Area using the 'Rustferris' image in the top left of each area, or anywhere else on an Area it seems! just not on text. The position of an area persists from where it was last dragged and positioned. click the center and hold and drag area around to desired position. egui::Area itself does not directly control its size β€” it wraps content and takes the size of whatever UI elements are inside it."); + egui::Frame::new().fill(egui::Color32::WHITE).show(ui, |ui| { + ui.label(RichText::new("Label with white background, Rust hex color") + .color(egui::Color32::from_rgb(183, 65, 14)) + .size(16.0) + ); + }); + + }); } diff --git a/src/rustbook_code_blocks.rs b/src/rustbook_code_blocks.rs index c5452d4..ec7cedb 100644 --- a/src/rustbook_code_blocks.rs +++ b/src/rustbook_code_blocks.rs @@ -5,6 +5,7 @@ pub const VARIABLES_MUTABILITY_DATATYPES: &str = r#" Variables, Mutability & DataTypes in Rust // This code would not compile, because by default, variables are immutable, and as the RustBook states, 'When a variable is immutable, once a value is bound to a name, you cant change that value.' + fn main() { let x = 5; println!("The value of x is: {x}"); @@ -12,7 +13,36 @@ fn main() { println!("The value of x is: {x}"); } -// Good code that would compile bc value is declared mutable, +// In the provided Rust example, a compilation error would occur because the code attempts to assign a new value to an 'immutable variable `x`', which is not allowed in Rust by default. + +// Error Message + +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) +error[E0384]: cannot assign twice to immutable variable `x` + --> src/main.rs:4:5 + | +2 | let x = 5; + | - first assignment to `x` +3 | println!("The value of x is: {x}"); +4 | x = 6; + | ^^^^^ cannot assign twice to immutable variable + | +help: consider making this binding mutable + | +2 | let mut x = 5; + | +++ + +For more information about this error, try `rustc --explain E0384`. +error: could not compile `variables` (bin "variables") due to 1 previous error + +// This happens because `x` was declared with `let x = 5;`, making it immutable, and then reassigned later with `x = 6;`. + +// Solution: + +To make the variable mutable, use `let mut x = 5;`. + +// Good code that would compile bc 'x' is declared mutable, fn main() { let mut x = 5; @@ -21,6 +51,64 @@ fn main() { println!("The value of x is: {x}"); } +$ cargo run + Compiling variables v0.1.0 (file:///projects/variables) + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s + Running `target/debug/variables` +The value of x is: 5 +The value of x is: 6 + +Why This Matters: + + * Rust enforces `immutability by default` to prevent bugs that arise from unexpected value changes. + * This makes code more predictable and easier to debug, especially in concurrent or complex systems. + * If you need to change a variable, explicitly marking it as mutable (`mut`) signals that intent to other developers. + +// Takeaway: +// Compiler errors in Rust are designed to help you write **safe and correct code**. Even experienced developers encounter them β€” they’re part of the learning process and a core strength of Rust's safety guarantees. + +**Constants** + +// Constants are values bound to a name and are not allowed to change, similar to immutable variables. There are differences though between constants and variables, so lets take a look. + +🧱 Key Characteristics + +| Feature | Constants | Variables | +| ------------------- | --------------------------------- | ------------------------ | +| Mutability | Always immutable | Immutable by default | +| Declaration Keyword | `const` | `let` or `let mut` | +| Type Annotation | **Required** | Optional (type inferred) | +| Scope | Any (including global scope) | Typically local scope | +| Value Requirement | Must be a **constant expression** | Can be a runtime value | + +Example: + +const FERRIS_APPROVAL_RATING: u8 = 100; + +**Shadowing** + +// Shadowing allows you to redeclare a variable with the same name, effectively "shadowing" the previous one. +// It can even change the **type** or **mutability**. + +let z = 5; +let z = z + 1; // shadows the previous z +let z = z * 2; // shadows again +println!("The value of z is: {}", z); // prints 12 + +let spaces = " "; +let spaces = spaces.len(); // `spaces` now holds an integer + +// Unlike `mut`, shadowing creates a **new variable**, which is useful for transformations. + +Summary: + +| Concept | Keyword | Mutable by Default | Can Change Type | Redeclaring Allowed | +|-------------|-------------|--------------------|-----------------|-------------------------| +| Variable | `let` | ❌ No | ❌ No | βœ… With shadowing | +| Mutable Var | `let mut` | βœ… Yes | ❌ No | ❌ | +| Constant | `const` | ❌ No | ❌ No | ❌ | +| Shadowing | `let` again | βœ… Yes | βœ… Yes | βœ… (via new declaration) | + ___________________________________________________________________________________ Data Types @@ -117,5 +205,95 @@ pub const FUNCTIONS: &str = r#" Functions are used prevalently in Rust. -There is one of the most important funtions, the 'main' function. +There is one of the most important funtions, the 'main' function, the entry point of many programs. In RUST we use the fn keyword to allow us to declare new functions. + +For function and variable names, RUST uses snake case, where all letters are lowercase and underscores seperate words. example_snake_case + +// ================================================ +// 1. Function Basics +// ================================================ + +// Functions are declared using the `fn` keyword. +// The name follows snake_case convention. +// Function definitions go before they are called. + +fn main() { + println!("Welcome to the Rust functions demo!"); + + // Calling a basic function + basic_function(); + + // Calling a function with parameters + print_measurement(5, 'h'); + + // Calling a function that returns a value + let result = plus_one(5); + println!("5 + 1 = {}", result); +} + +// ================================================ +// 2. Defining and Calling Functions +// ================================================ +fn basic_function() { + // This function takes no arguments and returns nothing + println!("This is a basic function call."); +} + +// ================================================ +// 3. Function Parameters +// ================================================ +// Parameters are variables that are part of a function's signature + +fn print_measurement(value: i32, unit_label: char) { + // Parameters must have types explicitly declared + println!("The measurement is: {}{}", value, unit_label); +} + +// ================================================ +// 4. Function Return Values +// ================================================ +// Functions can return values with the `->` syntax + +fn plus_one(x: i32) -> i32 { + // Implicit return: no semicolon means the value is returned + x + 1 + // If you put a semicolon here, it becomes a statement, not an expression, and the function will return `()` +} + +// ================================================ +// 5. Statements vs Expressions +// ================================================ +// - Statements: instructions that perform actions and do not return a value +// - Expressions: evaluate to a value + +// For example: +fn _statement_vs_expression() { + let y = { + let x = 3; + x + 1 // This is an expression, the value of this block is 4 + }; + println!("y = {}", y); +} + +// ================================================ +// 6. Practice: Writing Your Own Functions +// ================================================ +// Challenge yourself to: +// - Write a function that takes 3 parameters +// - Return a computed result + +// Example: +fn multiply_add(a: i32, b: i32, c: i32) -> i32 { + (a * b) + c +} + +// ================================================ +// 7. Recap +// ================================================ +// - `fn` to define functions +// - Parameters require explicit types +// - Use `->` to specify a return type +// - Expression without semicolon for return +// - Function calls must match parameter types + "#; diff --git a/src/side_panel.rs b/src/side_panel.rs index 8d3ca4a..17f67e1 100644 --- a/src/side_panel.rs +++ b/src/side_panel.rs @@ -14,7 +14,7 @@ impl SidePanelState { Self { spp1: false, spp2: false, - language: "rs".into() + language: "rs".into(), } } } @@ -43,81 +43,83 @@ pub fn side_panel_ui(ctx: &egui::Context, state: &mut SidePanelState) { if state.spp1 { egui::Window::new("πŸ“¦ Variables, Mutability & DataTypes") - .resizable(true) - .vscroll(true) - .show(ctx, |ui| { - ui.label("πŸ“¦ Variables, Mutability & DataTypes"); - let theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style()); - let code = rustbook_code_blocks::VARIABLES_MUTABILITY_DATATYPES; - let mut layouter = |ui: &egui::Ui, text: &str, wrap_width: f32| { - let mut layout_job = egui_extras::syntax_highlighting::highlight( - ui.ctx(), - ui.style(), - &theme, - text, - &state.language, - ); - layout_job.wrap.max_width = wrap_width; - ui.fonts(|f| f.layout_job(layout_job)) - }; - - egui::ScrollArea::vertical() - .max_height(500.0) // Limit height for scrollability - .show(ui, |ui| { - ui.add_sized( - [ui.available_width(), 800.0], // Large height to simulate overflow - egui::TextEdit::multiline(&mut code.to_string()) - .font(egui::TextStyle::Monospace) // Use monospaced font - .code_editor() // Enables code styling - .desired_rows(13) - .lock_focus(true) - .desired_width(f32::INFINITY) - .layouter(&mut layouter), + .resizable(true) + .vscroll(true) + .show(ctx, |ui| { + ui.label("πŸ“¦ Variables, Mutability & DataTypes"); + let theme = + egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style()); + let code = rustbook_code_blocks::VARIABLES_MUTABILITY_DATATYPES; + let mut layouter = |ui: &egui::Ui, text: &str, wrap_width: f32| { + let mut layout_job = egui_extras::syntax_highlighting::highlight( + ui.ctx(), + ui.style(), + &theme, + text, + &state.language, ); - }); - if ui.button("❌ Close").clicked() { - state.spp1 = false; - } - }); + layout_job.wrap.max_width = wrap_width; + ui.fonts(|f| f.layout_job(layout_job)) + }; + + egui::ScrollArea::vertical() + .max_height(600.0) // Limit height for scrollability + .show(ui, |ui| { + ui.add_sized( + [ui.available_width(), 800.0], + egui::TextEdit::multiline(&mut code.to_string()) + .font(egui::TextStyle::Monospace) // Use monospaced font + .code_editor() // Enables code styling + .desired_rows(13) + .lock_focus(true) + .desired_width(f32::INFINITY) + .layouter(&mut layouter), + ); + }); + if ui.button("❌ Close").clicked() { + state.spp1 = false; + } + }); } if state.spp2 { egui::Window::new("πŸ“¦ Functions") - .resizable(true) - .vscroll(true) - .show(ctx, |ui| { - ui.label("πŸ“¦ Functions"); - let theme = egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style()); - let code = rustbook_code_blocks::FUNCTIONS; - let mut layouter = |ui: &egui::Ui, text: &str, wrap_width: f32| { - let mut layout_job = egui_extras::syntax_highlighting::highlight( - ui.ctx(), - ui.style(), - &theme, - text, - &state.language, - ); - layout_job.wrap.max_width = wrap_width; - ui.fonts(|f| f.layout_job(layout_job)) - }; - - egui::ScrollArea::vertical() - .max_height(300.0) // Limit height for scrollability - .show(ui, |ui| { - ui.add_sized( - [ui.available_width(), 800.0], // Large height to simulate overflow - egui::TextEdit::multiline(&mut code.to_string()) - .font(egui::TextStyle::Monospace) // Use monospaced font - .code_editor() // Enables code styling - .desired_rows(13) - .lock_focus(true) - .desired_width(f32::INFINITY) - .layouter(&mut layouter), + .resizable(true) + .vscroll(true) + .show(ctx, |ui| { + ui.label("πŸ“¦ Functions"); + let theme = + egui_extras::syntax_highlighting::CodeTheme::from_memory(ui.ctx(), ui.style()); + let code = rustbook_code_blocks::FUNCTIONS; + let mut layouter = |ui: &egui::Ui, text: &str, wrap_width: f32| { + let mut layout_job = egui_extras::syntax_highlighting::highlight( + ui.ctx(), + ui.style(), + &theme, + text, + &state.language, ); - }); - if ui.button("❌ Close").clicked() { - state.spp2 = false; - } - }); + layout_job.wrap.max_width = wrap_width; + ui.fonts(|f| f.layout_job(layout_job)) + }; + + egui::ScrollArea::vertical() + .max_height(600.0) // Limit height for scrollability + .show(ui, |ui| { + ui.add_sized( + [ui.available_width(), 800.0], + egui::TextEdit::multiline(&mut code.to_string()) + .font(egui::TextStyle::Monospace) // Use monospaced font + .code_editor() // Enables code styling + .desired_rows(13) + .lock_focus(true) + .desired_width(f32::INFINITY) + .layouter(&mut layouter), + ); + }); + if ui.button("❌ Close").clicked() { + state.spp2 = false; + } + }); } } diff --git a/src/top_panel.rs b/src/top_panel.rs index 625f0bc..e74cc9f 100644 --- a/src/top_panel.rs +++ b/src/top_panel.rs @@ -3,6 +3,7 @@ use eframe::egui; pub fn top_panel_ui(ctx: &egui::Context) { egui::TopBottomPanel::top("top_panel").show(ctx, |ui| { // The top panel is often a good place for a menu bar: + const FERRIS_SAYS: &str = "Keep calm and match on."; egui::menu::bar(ui, |ui| { // NOTE: no File->Quit on web pages! @@ -17,6 +18,7 @@ pub fn top_panel_ui(ctx: &egui::Context) { } egui::widgets::global_theme_preference_buttons(ui); + ui.label(egui::RichText::new(FERRIS_SAYS).italics()); }); }); }