#[derive(Debug, Clone)]
pub struct Button {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
pub text: String,
pub color: [u8; 4],
pub hover_color: [u8; 4],
pub is_hovered: bool,
pub is_pressed: bool,
}
impl Button {
pub fn new(x: i32, y: i32, width: u32, height: u32, text: &str) -> Self {
Self {
x,
y,
width,
height,
text: text.to_string(),
color: [100, 100, 100, 255],
hover_color: [150, 150, 150, 255],
is_hovered: false,
is_pressed: false,
}
}
pub fn with_color(mut self, color: [u8; 4]) -> Self {
self.color = color;
self
}
pub fn with_hover_color(mut self, color: [u8; 4]) -> Self {
self.hover_color = color;
self
}
pub fn contains_point(&self, mouse_x: i32, mouse_y: i32) -> bool {
mouse_x >= self.x
&& mouse_x <= self.x + self.width as i32
&& mouse_y >= self.y
&& mouse_y <= self.y + self.height as i32
}
pub fn update(&mut self, mouse_x: i32, mouse_y: i32, mouse_pressed: bool) -> bool {
self.is_hovered = self.contains_point(mouse_x, mouse_y);
let was_pressed = self.is_pressed;
self.is_pressed = self.is_hovered && mouse_pressed;
was_pressed && !self.is_pressed && self.is_hovered
}
}
pub fn render_button(button: &Button, pixels: &mut [u8], viewport_width: u32) {
let color = if button.is_hovered {
button.hover_color
} else {
button.color
};
for y in 0..button.height {
for x in 0..button.width {
let px = button.x + x as i32;
let py = button.y + y as i32;
if px < 0 || py < 0 || px >= viewport_width as i32 {
continue;
}
let index = ((py as u32 * viewport_width) + px as u32) as usize * 4;
if index + 3 < pixels.len() {
pixels[index..index + 4].copy_from_slice(&color);
}
}
}
}
pub fn render_text(text: &str, x: i32, y: i32, color: [u8; 4], pixels: &mut [u8], viewport_width: u32) {
for (i, ch) in text.chars().enumerate() {
let char_x = x + (i as i32 * 8);
render_char(ch, char_x, y, color, pixels, viewport_width);
}
}
fn render_char(ch: char, x: i32, y: i32, color: [u8; 4], pixels: &mut [u8], viewport_width: u32) {
let pattern = get_char_pattern(ch);
for row in 0..8 {
for col in 0..8 {
if pattern[row] & (1 << (7 - col)) != 0 {
let px = x + col as i32;
let py = y + row as i32;
if px >= 0 && py >= 0 && px < viewport_width as i32 {
let index = ((py as u32 * viewport_width) + px as u32) as usize * 4;
if index + 3 < pixels.len() {
pixels[index..index + 4].copy_from_slice(&color);
}
}
}
}
}
}
fn get_char_pattern(ch: char) -> [u8; 8] {
match ch {
'A' => [0x18, 0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x00],
'B' => [0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00],
'C' => [0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00],
'D' => [0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00],
'E' => [0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00],
'F' => [0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00],
'G' => [0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00],
'H' => [0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00],
'I' => [0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00],
'J' => [0x1E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00],
'K' => [0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00],
'L' => [0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00],
'M' => [0x63, 0x77, 0x7F, 0x6B, 0x63, 0x63, 0x63, 0x00],
'N' => [0x66, 0x76, 0x7E, 0x7E, 0x6E, 0x66, 0x66, 0x00],
'O' => [0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00],
'P' => [0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00],
'Q' => [0x3C, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x0E, 0x00],
'R' => [0x7C, 0x66, 0x66, 0x7C, 0x78, 0x6C, 0x66, 0x00],
'S' => [0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00],
'T' => [0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00],
'U' => [0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00],
'V' => [0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00],
'W' => [0x63, 0x63, 0x63, 0x6B, 0x7F, 0x77, 0x63, 0x00],
'X' => [0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00],
'Y' => [0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00],
'Z' => [0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00],
'0' => [0x3C, 0x66, 0x6E, 0x76, 0x66, 0x66, 0x3C, 0x00],
'1' => [0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00],
'2' => [0x3C, 0x66, 0x06, 0x0C, 0x30, 0x60, 0x7E, 0x00],
'3' => [0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00],
'4' => [0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x0C, 0x00],
'5' => [0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00],
'6' => [0x3C, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00],
'7' => [0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00],
'8' => [0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00],
'9' => [0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00],
' ' => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
':' => [0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00, 0x00],
'!' => [0x18, 0x18, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00],
_ => [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
}
}
pub fn render_rect(x: i32, y: i32, width: u32, height: u32, color: [u8; 4], pixels: &mut [u8], viewport_width: u32) {
for dy in 0..height {
for dx in 0..width {
let px = x + dx as i32;
let py = y + dy as i32;
if px < 0 || py < 0 || px >= viewport_width as i32 {
continue;
}
let index = ((py as u32 * viewport_width) + px as u32) as usize * 4;
if index + 3 < pixels.len() {
pixels[index..index + 4].copy_from_slice(&color);
}
}
}
}
pub fn render_rect_outline(x: i32, y: i32, width: u32, height: u32, thickness: u32, color: [u8; 4], pixels: &mut [u8], viewport_width: u32) {
render_rect(x, y, width, thickness, color, pixels, viewport_width);
render_rect(x, y + height as i32 - thickness as i32, width, thickness, color, pixels, viewport_width);
render_rect(x, y, thickness, height, color, pixels, viewport_width);
render_rect(x + width as i32 - thickness as i32, y, thickness, height, color, pixels, viewport_width);
}