#[derive(Default)]
pub struct Button {
pub label: Option<crate::ui::text::Text>,
pub label_size: f32,
pub text_alignment: Alignment,
pub pos_x: usize,
pub pos_y: usize,
pub width: usize,
pub height: usize,
pub border_size_idle: usize,
pub border_size_hovered: usize,
pub border_size_clicked: usize,
pub shadow_size_idle: usize,
pub shadow_size_hovered: usize,
pub shadow_size_clicked: usize,
pub shadow_intensity_idle: u8,
pub shadow_intensity_hovered: u8,
pub shadow_intensity_clicked: u8,
pub label_col_idle: crate::color::Color,
pub border_col_idle: crate::color::Color,
pub bg_col_idle: crate::color::Color,
pub label_col_hovered: crate::color::Color,
pub border_col_hovered: crate::color::Color,
pub bg_col_hovered: crate::color::Color,
pub label_col_clicked: crate::color::Color,
pub border_col_clicked: crate::color::Color,
pub bg_col_clicked: crate::color::Color,
pub button_type: ButtonType,
pub state: ButtonState,
pub toggled: bool,
pub radius: usize,
}
impl Button {
pub fn label(mut self, text: &str, font: crate::ttf::Font, size: f32) -> Self {
self.label = Some(crate::ui::text::Text::new(text, font));
self.label_size = size;
self
}
pub fn label_alignment(mut self, alignment: Alignment) -> Self {
self.text_alignment = alignment;
self
}
pub fn position(mut self, x: usize, y: usize) -> Self {
self.pos_x = x;
self.pos_y = y;
self
}
pub fn size(mut self, width: usize, height: usize) -> Self {
self.width = width;
self.height = height;
self
}
pub fn border(mut self, size: usize) -> Self {
self.border_size_idle = size;
self.border_size_hovered = size;
self.border_size_clicked = size;
self
}
pub fn shadow(mut self, size: usize, intensity: u8) -> Self {
self.shadow_size_idle = size;
self.shadow_size_hovered = size;
self.shadow_size_clicked = size;
self.shadow_intensity_idle = intensity;
self.shadow_intensity_hovered = intensity;
self.shadow_intensity_clicked = intensity;
self
}
pub fn idle_shadow(mut self, size: usize, intensity: u8) -> Self {
self.shadow_size_idle = size;
self.shadow_intensity_idle = intensity;
self
}
pub fn hover_shadow(mut self, size: usize, intensity: u8) -> Self {
self.shadow_size_hovered = size;
self.shadow_intensity_hovered = intensity;
self
}
pub fn click_shadow(mut self, size: usize, intensity: u8) -> Self {
self.shadow_size_clicked = size;
self.shadow_intensity_clicked = intensity;
self
}
pub fn label_color(mut self, color: crate::color::Color) -> Self {
self.label_col_hovered = color.clone();
self.label_col_clicked = color.clone();
self.label_col_idle = color;
self
}
pub fn idle_label_col(mut self, color: crate::color::Color) -> Self {
self.label_col_idle = color;
self
}
pub fn hover_label_col(mut self, color: crate::color::Color) -> Self {
self.label_col_hovered = color;
self
}
pub fn click_label_col(mut self, color: crate::color::Color) -> Self {
self.label_col_clicked = color;
self
}
pub fn border_color(mut self, color: crate::color::Color) -> Self {
self.border_col_hovered = color.clone();
self.border_col_clicked = color.clone();
self.border_col_idle = color;
self
}
pub fn background(mut self, color: crate::color::Color) -> Self {
self.bg_col_hovered = color.clone();
self.bg_col_clicked = color.clone();
self.bg_col_idle = color;
self
}
pub fn idle_bg(mut self, color: crate::color::Color) -> Self {
self.bg_col_idle = color;
self
}
pub fn hover_bg(mut self, color: crate::color::Color) -> Self {
self.bg_col_hovered = color;
self
}
pub fn click_bg(mut self, color: crate::color::Color) -> Self {
self.bg_col_clicked = color;
self
}
pub fn button_type(mut self, button_type: ButtonType) -> Self {
self.button_type = button_type;
self
}
pub fn radius(mut self, radius: usize) -> Self {
self.radius = radius;
self
}
fn draw_shadow(&self, window: &mut crate::window::Window) {
let shadow_depth;
let shadow_size;
let border_size;
match self.state {
ButtonState::Idle => {
shadow_depth = self.shadow_intensity_idle;
shadow_size = self.shadow_size_idle;
border_size = self.border_size_idle;
},
ButtonState::Hovered => {
shadow_depth = self.shadow_intensity_hovered;
shadow_size = self.shadow_size_hovered;
border_size = self.border_size_hovered;
},
ButtonState::Clicked => {
shadow_depth = self.shadow_intensity_clicked;
shadow_size = self.shadow_size_clicked;
border_size = self.border_size_clicked;
}
}
let inner_x = self.pos_x + border_size;
let inner_y = self.pos_y + border_size;
let inner_w = self.width.saturating_sub(border_size * 2);
let inner_h = self.height.saturating_sub(border_size * 2);
let inner_r = self.radius.saturating_sub(border_size) as f32;
let cx = inner_x as f32 + inner_w as f32 / 2.0;
let cy = inner_y as f32 + inner_h as f32 / 2.0;
let hw = inner_w as f32 / 2.0;
let hh = inner_h as f32 / 2.0;
for py in inner_y..inner_y + inner_h {
for px in inner_x..inner_x + inner_w {
let pfx = px as f32 + 0.5;
let pfy = py as f32 + 0.5;
let dx = (pfx - cx).abs() - (hw - inner_r);
let dy = (pfy - cy).abs() - (hh - inner_r);
let outside = (dx.max(0.0).powi(2) + dy.max(0.0).powi(2)).sqrt();
let inside = dx.max(dy).min(0.0);
let dist = outside + inside - inner_r;
let depth = -dist; if depth > 0.0 && depth <= shadow_size as f32 {
let t = 1.0 - (depth / shadow_size as f32);
let blend = (shadow_depth as f32 * t) as i32;
if blend > 0 {
darken_pixel(window, px, py, blend);
}
}
}
}
}
fn draw_button(&self, window: &mut crate::window::Window) {
let bg_col: &crate::color::Color;
let border_col: &crate::color::Color;
let label_col: &crate::color::Color;
let border_size;
match self.state {
ButtonState::Idle => {
bg_col = &self.bg_col_idle;
border_col = &self.border_col_idle;
label_col = &self.label_col_idle;
border_size = self.border_size_idle;
}
ButtonState::Hovered =>{
bg_col = &self.bg_col_hovered;
border_col = &self.border_col_hovered;
label_col = &self.label_col_hovered;
border_size = self.border_size_hovered;
},
ButtonState::Clicked => {
bg_col = &self.bg_col_clicked;
border_col = &self.border_col_clicked;
label_col = &self.label_col_clicked;
border_size = self.border_size_clicked;
},
}
window.draw_rect_f(
self.pos_x,
self.pos_y,
self.width,
self.height,
self.radius,
bg_col,
0,
);
for i in 0..border_size {
window.draw_rect(
self.pos_x + i,
self.pos_y + i,
self.width - i * 2,
self.height - i * 2,
self.radius.saturating_sub(i),
border_col,
);
}
if let Some(label) = &self.label {
let lm = label.font.font.horizontal_line_metrics(self.label_size).unwrap();
let y_pos = (self.pos_y as f32 + (self.height as f32 / 2.0) - (lm.ascent / 2.0)
+ (lm.descent / 3.0))
.max(0.0) as usize;
let text_width = label.get_width_precise(self.label_size);
match self.text_alignment {
Alignment::Left => {
window.draw_text(
self.pos_x + border_size + 4,
y_pos,
&label,
self.label_size,
label_col,
);
}
Alignment::Right => {
let x = (self.pos_x + self.width) as f32 - text_width - 4.0;
window.draw_text(
x.max(0.0) as usize,
y_pos,
&label,
self.label_size,
label_col,
);
}
Alignment::Center => {
let x = self.pos_x as f32 + (self.width as f32 / 2.0) - (text_width / 2.0);
window.draw_text(
x.max(0.0) as usize,
y_pos,
&label,
self.label_size,
label_col,
);
}
}
}
}
pub fn is_hovered(&self, window: &crate::window::Window) -> bool {
let state = window.get_mouse_state();
(state.pos_x as usize) > self.pos_x
&& (state.pos_y as usize) > self.pos_y
&& (state.pos_x as usize) < self.pos_x + self.width
&& (state.pos_y as usize) < self.pos_y + self.height
}
pub fn is_left_clicked(&self, window: &crate::window::Window) -> bool {
let state = window.get_mouse_state();
self.is_hovered(window) && state.lmb_clicked
}
pub fn is_right_clicked(&self, window: &crate::window::Window) -> bool {
let state = window.get_mouse_state();
self.is_hovered(window) && state.rmb_clicked
}
fn update(&mut self, window: &mut crate::window::Window) {
let mouse = window.get_mouse_state();
let hovered = (mouse.pos_x as usize) > self.pos_x
&& (mouse.pos_y as usize) > self.pos_y
&& (mouse.pos_x as usize) < self.pos_x + self.width
&& (mouse.pos_y as usize) < self.pos_y + self.height;
let clicked = hovered && (mouse.lmb_clicked || mouse.rmb_clicked);
match self.button_type {
ButtonType::Push => {
self.state = if clicked {
ButtonState::Clicked
} else if hovered {
ButtonState::Hovered
} else {
ButtonState::Idle
};
}
ButtonType::Toggle => {
if clicked && !self.toggled {
self.toggled = true;
} else if clicked && self.toggled {
self.toggled = false;
}
self.state = if self.toggled {
ButtonState::Clicked
} else if hovered {
ButtonState::Hovered
} else {
ButtonState::Idle
};
}
}
}
pub fn draw(&mut self, window: &mut crate::window::Window) {
self.update(window);
self.draw_button(window);
self.draw_shadow(window);
}
}
#[derive(Default)]
pub enum ButtonType {
#[default]
Push,
Toggle,
}
#[derive(Default)]
pub enum Alignment {
Left,
Right,
#[default]
Center,
}
#[derive(Default)]
pub enum ButtonState {
#[default]
Idle,
Hovered,
Clicked,
}
fn darken_pixel(window: &mut crate::window::Window, x: usize, y: usize, blend: i32) {
let existing = window.get_pixel(x, y);
let er = ((existing >> 16) & 0xFF) as i32;
let eg = ((existing >> 8) & 0xFF) as i32;
let eb = ((existing) & 0xFF) as i32;
let r = (er - blend).clamp(0, 255) as u32;
let g = (eg - blend).clamp(0, 255) as u32;
let b = (eb - blend).clamp(0, 255) as u32;
window.draw_pixel(
x,
y,
&crate::color::Color::from(0xFF000000 | (r << 16) | (g << 8) | b),
);
}