Basic slide setup

This commit is contained in:
Joey Hines 2026-05-03 12:26:45 -06:00
parent 090c8a6849
commit 1751dfaa62
Signed by: joeyahines
GPG Key ID: E99D8FB14855100E
13 changed files with 254 additions and 10 deletions

7
cfg/01_author_intro.toml Normal file
View File

@ -0,0 +1,7 @@
title = "Who am I? (Please help)"
image = "cfg/img/joey_headshot.png"
body = """
* Joey Hines - Onboard Software Engineer at Loft Orbital
* Writes code everyday like a nerd
* Makes real money, mainly by wasting VC's money
"""

View File

@ -0,0 +1,7 @@
title = "What I do at work"
image = "cfg/img/methanesat.png"
body = """
* Write Flight Software (FSW) for some very successful satellites
* Collabroate with diffrent teams to execute on satellite missions
* Keep the work equivalent of #fucky-wucky up to date
"""

View File

@ -0,0 +1,9 @@
title = "Who am I? (Outside Work B-))"
image = "cfg/img/joey_and_molly.png"
body = """
* Joey Hines - Nerd
* Has a girlfriend (nice)
* Likes TTRPGS and Boardgames
* Writes code like a nerd
* Keep #fucky-wucky Up to Date
"""

BIN
cfg/img/joey_and_molly.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 MiB

BIN
cfg/img/joey_headshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 MiB

BIN
cfg/img/methanesat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 529 KiB

View File

@ -3,13 +3,16 @@ mod slide;
mod slide_show_config; mod slide_show_config;
mod slides; mod slides;
mod text_drawer; mod text_drawer;
mod utils;
use macroquad::prelude::*;
use crate::slide_show_config::{Context, SlideShowTheme}; use crate::slide_show_config::{Context, SlideShowTheme};
use crate::slides::Slideshow; use crate::slides::Slideshow;
use crate::slides::demo::Demo;
use crate::slides::standard_slide::StandardSlide;
use crate::slides::title::Title; use crate::slides::title::Title;
use crate::text_drawer::{Justified, TextConfigBuilder}; use crate::text_drawer::{Justified, TextConfigBuilder};
use macroquad::prelude::*;
use std::path::PathBuf;
fn window_conf() -> Conf { fn window_conf() -> Conf {
Conf { Conf {
@ -38,12 +41,12 @@ async fn main() {
.justification(Justified::CenterHorizontal) .justification(Justified::CenterHorizontal)
.font(&heading) .font(&heading)
.color(WHITE) .color(WHITE)
.font_size(100) .font_size(150)
.build(), .build(),
body_config: TextConfigBuilder::default() body_config: TextConfigBuilder::default()
.justification(Justified::Left) .justification(Justified::Left)
.font(&body) .font(&body)
.font_size(50) .font_size(100)
.color(WHITE) .color(WHITE)
.build(), .build(),
}; };
@ -51,6 +54,10 @@ async fn main() {
let mut slideshow = Slideshow::new(Context::new(slide_show_theme)); let mut slideshow = Slideshow::new(Context::new(slide_show_theme));
slideshow.add_slide(Title::new("Software As a Bit".to_string())); slideshow.add_slide(Title::new("Software As a Bit".to_string()));
slideshow.add_slide(Demo::new());
slideshow.add_slide(StandardSlide::new(&PathBuf::from("cfg/01_author_intro.toml")).await);
slideshow.add_slide(StandardSlide::new(&PathBuf::from("cfg/02_things_i_have_worked_on.toml")).await);
slideshow.add_slide(StandardSlide::new(&PathBuf::from("cfg/03_me_outside_work.toml")).await);
loop { loop {
if slideshow.handle_slide_show() { if slideshow.handle_slide_show() {

57
src/slides/demo.rs Normal file
View File

@ -0,0 +1,57 @@
use crate::effects::star_field;
use crate::slide::Slide;
use crate::slide_show_config::Context;
use crate::utils::screen_center;
use macroquad::color::BLACK;
use macroquad::prelude::*;
#[derive(Debug, Default)]
pub struct Demo {
camera_angle: f32,
}
impl Demo {
pub fn new() -> Box<Self> {
Box::new(Self::default())
}
}
impl Slide for Demo {
fn display(&mut self, ctx: &Context<'_>) {
clear_background(BLACK);
star_field();
push_camera_state();
let camera_x = self.camera_angle.sin() * 20.0;
let camera_z = self.camera_angle.cos() * 20.0;
set_camera(&Camera3D {
position: vec3(camera_x, 15., camera_z),
up: vec3(0., 1., 0.),
target: vec3(0.0, 0.0, 0.0),
..Default::default()
});
draw_sphere_wires(vec3(0.0, 0.0, 0.0), 6.0, None, RED);
draw_sphere_wires(vec3(15.0, 0.0, 0.0), 2.0, None, RED);
draw_sphere_wires(vec3(0.0, 0.0, 15.0), 2.0, None, RED);
draw_sphere_wires(vec3(-15.0, 0.0, 0.0), 2.0, None, RED);
draw_sphere_wires(vec3(0.0, 0.0, -15.0), 2.0, None, RED);
draw_sphere_wires(vec3(15.0, 0.0, 0.0), 2.0, None, RED);
let diff = 1.0 * get_frame_time();
self.camera_angle = (self.camera_angle + diff) % 360.0;
pop_camera_state();
ctx
.slide_show_theme
.heading_config
.draw_text("This Ain't No Powerpoint", screen_center());
}
}

View File

@ -1,7 +1,10 @@
use crate::slide::Slide; use crate::slide::Slide;
use crate::slide_show_config::Context; use crate::slide_show_config::Context;
use macroquad::camera::set_default_camera;
use macroquad::input::{KeyCode, is_key_pressed}; use macroquad::input::{KeyCode, is_key_pressed};
pub mod demo;
pub mod standard_slide;
pub mod title; pub mod title;
pub struct Slideshow<'a> { pub struct Slideshow<'a> {
@ -24,9 +27,15 @@ impl<'a> Slideshow<'a> {
} }
pub fn handle_slide_show(&mut self) -> bool { pub fn handle_slide_show(&mut self) -> bool {
set_default_camera();
if is_key_pressed(KeyCode::Escape) { if is_key_pressed(KeyCode::Escape) {
return true; return true;
}; } else if is_key_pressed(KeyCode::Right) {
self.current_slide += 1;
} else if is_key_pressed(KeyCode::Left) {
self.current_slide = self.current_slide.saturating_sub(1);
}
self.current_slide = self.current_slide.clamp(0, self.slides.len() - 1);
self.slides[self.current_slide].display(&self.ctx); self.slides[self.current_slide].display(&self.ctx);

View File

@ -0,0 +1,92 @@
use crate::effects::star_field;
use crate::slide::Slide;
use crate::slide_show_config::Context;
use config::Config;
use macroquad::color::{BLACK, WHITE};
use macroquad::file::load_file;
use macroquad::math::Vec2;
use macroquad::prelude::{Texture2D, clear_background, load_texture, draw_texture, draw_texture_ex, DrawTextureParams};
use macroquad::window::{screen_height, screen_width};
use serde::Deserialize;
use std::path::{Path, PathBuf};
#[derive(Debug, Deserialize)]
pub struct StandardSlideConfig {
title: String,
body: String,
image: Option<PathBuf>,
}
impl StandardSlideConfig {
pub fn new(src: &str) -> Result<Self, config::ConfigError> {
let cfg = Config::builder()
.add_source(config::File::from_str(src, config::FileFormat::Toml))
.build()?;
cfg.try_deserialize()
}
}
#[derive(Debug)]
pub struct StandardSlide {
cfg: StandardSlideConfig,
image: Option<Texture2D>,
}
impl StandardSlide {
pub async fn new(cfg_path: &Path) -> Box<Self> {
let cfg_bytes = load_file(cfg_path.to_str().unwrap()).await.unwrap();
let cfg_str = String::from_utf8(cfg_bytes).unwrap();
let cfg = StandardSlideConfig::new(&cfg_str).unwrap();
let image = if let Some(image_path) = &cfg.image {
Some(load_texture(image_path.to_str().unwrap()).await.unwrap())
} else {
None
};
Box::new(Self { cfg, image })
}
}
impl Slide for StandardSlide {
fn display(&mut self, ctx: &Context<'_>) {
clear_background(BLACK);
star_field();
let title_pos = Vec2::new(screen_width() * 0.5, screen_height() * 0.10);
let info = ctx
.slide_show_theme
.heading_config
.draw_text(&self.cfg.title, title_pos);
let body_pos = Vec2::new(
screen_width() * 0.05,
screen_height() * 0.33 + info.text_dimensions.height,
);
ctx.slide_show_theme.body_config.draw_text_box(
&self.cfg.body,
body_pos,
Vec2::new(screen_width() * 0.5, screen_height() * 0.8),
);
if let Some(image) = &self.image {
let size = image.size();
let image_center_pos = Vec2::new(screen_width() * 0.75, screen_height() * 0.5);
let target_image_size = Vec2::new(screen_width() * 0.33, screen_height() * 0.66);
let scale = f32::min(target_image_size.x / size.x, target_image_size.y / size.y);
let new_image_size = size * scale;
let image_pos = image_center_pos - new_image_size * 0.5;
draw_texture_ex(image, image_pos.x, image_pos.y, WHITE, DrawTextureParams {
dest_size: Some(new_image_size),
..Default::default()
});
}
}
}

View File

@ -34,20 +34,22 @@ impl Slide for Title {
shadow_style.draw_text(&self.title, center_offset); shadow_style.draw_text(&self.title, center_offset);
title_style.draw_text(&self.title, center); title_style.draw_text(&self.title, center);
let subtitle_builder: TextConfigBuilder =
ctx.slide_show_theme.heading_config.clone().into();
let subtitle_style = subtitle_builder.font_size(100).build();
let author_pos = center + Vec2::new(0.0, screen_height() * 0.33); let author_pos = center + Vec2::new(0.0, screen_height() * 0.33);
ctx.slide_show_theme subtitle_style.draw_text("By Joey Hines", author_pos);
.heading_config
.draw_text("By Joey Hines", author_pos);
let bits = ["0", "1"]; let bits = ["0", "1"];
let bit_text_size = ctx.slide_show_theme.heading_config.measure_text("0"); let bit_text_size = subtitle_style.measure_text("0");
let count_needed = (screen_width() / bit_text_size.width).ceil() as usize; let count_needed = (screen_width() / bit_text_size.width).ceil() as usize;
let mut bit_pos = Vec2::new(0.0, center.y) + Vec2::new(0.0, screen_height() * 0.05); let mut bit_pos = Vec2::new(0.0, center.y) + Vec2::new(0.0, screen_height() * 0.05);
for _ in 0..count_needed { for _ in 0..count_needed {
let bit = bits.choose().unwrap(); let bit = bits.choose().unwrap();
let info = ctx.slide_show_theme.heading_config.draw_text(bit, bit_pos); let info = subtitle_style.draw_text(bit, bit_pos);
bit_pos = info.pos + Vec2::new(info.text_dimensions.width, 0.0); bit_pos = info.pos + Vec2::new(info.text_dimensions.width, 0.0);
} }

View File

@ -130,6 +130,54 @@ impl<'a> TextConfig<'a> {
} }
} }
pub fn draw_text_box(&self, text: &str, pos: Vec2, size: Vec2) -> TextInfo {
let mut lines: Vec<String> = text.split("\n").map(|s| s.to_string()).collect();
let mut line_pos = pos;
let mut line_ndx = 0;
loop {
if line_ndx >= lines.len() {
break;
}
let line = &lines[line_ndx];
let line_size = self.measure_text(line);
let line: Vec<&str> = line.split(" ").collect();
let mut word_pos = line_pos;
for word_ndx in 0..line.len() {
let word = line[word_ndx];
let word_info = self.draw_text(&format!("{word} "), word_pos);
word_pos.x += word_info.text_dimensions.width;
if word_pos.x > size.x {
let new_line = format!("\t\t\t{}", line[word_ndx + 1..].join(" "));
lines.insert(line_ndx + 1, new_line);
break;
}
}
line_ndx += 1;
line_pos.y += line_size.height * 1.2;
if line_pos.y > size.y {
// Stop drawing text
break;
}
}
TextInfo {
text_dimensions: TextDimensions {
width: size.x,
height: size.y,
offset_y: 0.0,
},
pos,
}
}
pub fn measure_text(&self, text: &str) -> TextDimensions { pub fn measure_text(&self, text: &str) -> TextDimensions {
measure_text(text, self.font, self.font_size, self.font_scale) measure_text(text, self.font, self.font_size, self.font_scale)
} }

6
src/utils.rs Normal file
View File

@ -0,0 +1,6 @@
use macroquad::math::Vec2;
use macroquad::miniquad::window::screen_size;
pub fn screen_center() -> Vec2 {
Vec2::from(screen_size()) * 0.5
}