#[derive(Clone, Copy, Debug)]
pub enum Anchor {
TopLeft,
TopCenter,
TopRight,
CenterLeft,
Center,
CenterRight,
BottomLeft,
BottomCenter,
BottomRight,
}
#[derive(Clone, Debug)]
pub struct UiElement {
pub id: String,
pub kind: UiKind,
pub anchor: Anchor,
pub offset: [f32; 2], pub size: [f32; 2], pub visible: bool,
pub color: [f32; 4], }
#[derive(Clone, Debug)]
pub enum UiKind {
Text { content: String, font_size: f32 },
Button {
label: String,
pressed: bool,
hovered: bool,
},
Slider {
value: f32,
min: f32,
max: f32,
label: String,
},
ProgressBar {
value: f32, fill_color: [f32; 4],
label: Option<String>,
},
Image { texture_source: String },
}
pub struct UiCanvas {
pub elements: Vec<UiElement>,
}
impl UiCanvas {
pub fn new() -> Self {
Self {
elements: Vec::new(),
}
}
}
impl Default for UiCanvas {
fn default() -> Self {
Self::new()
}
}
impl UiCanvas {
pub fn add_text(
&mut self,
id: &str,
content: &str,
anchor: Anchor,
offset: [f32; 2],
font_size: f32,
color: [f32; 4],
) -> &mut Self {
self.elements.push(UiElement {
id: id.to_string(),
kind: UiKind::Text {
content: content.to_string(),
font_size,
},
anchor,
offset,
size: [0.0, 0.0], visible: true,
color,
});
self
}
pub fn add_button(
&mut self,
id: &str,
label: &str,
anchor: Anchor,
offset: [f32; 2],
size: [f32; 2],
color: [f32; 4],
) -> &mut Self {
self.elements.push(UiElement {
id: id.to_string(),
kind: UiKind::Button {
label: label.to_string(),
pressed: false,
hovered: false,
},
anchor,
offset,
size,
visible: true,
color,
});
self
}
pub fn add_slider(
&mut self,
id: &str,
label: &str,
value: f32,
min: f32,
max: f32,
anchor: Anchor,
offset: [f32; 2],
size: [f32; 2],
) -> &mut Self {
self.elements.push(UiElement {
id: id.to_string(),
kind: UiKind::Slider {
value,
min,
max,
label: label.to_string(),
},
anchor,
offset,
size,
visible: true,
color: [0.3, 0.3, 0.3, 1.0],
});
self
}
pub fn add_progress_bar(
&mut self,
id: &str,
value: f32,
fill_color: [f32; 4],
anchor: Anchor,
offset: [f32; 2],
size: [f32; 2],
) -> &mut Self {
self.elements.push(UiElement {
id: id.to_string(),
kind: UiKind::ProgressBar {
value,
fill_color,
label: None,
},
anchor,
offset,
size,
visible: true,
color: [0.2, 0.2, 0.2, 0.8],
});
self
}
pub fn get(&self, id: &str) -> Option<&UiElement> {
self.elements.iter().find(|e| e.id == id)
}
pub fn get_mut(&mut self, id: &str) -> Option<&mut UiElement> {
self.elements.iter_mut().find(|e| e.id == id)
}
pub fn is_button_pressed(&self, id: &str) -> bool {
if let Some(el) = self.get(id) {
if let UiKind::Button { pressed, .. } = &el.kind {
return *pressed;
}
}
false
}
pub fn get_slider_value(&self, id: &str) -> Option<f32> {
if let Some(el) = self.get(id) {
if let UiKind::Slider { value, .. } = &el.kind {
return Some(*value);
}
}
None
}
pub fn set_progress(&mut self, id: &str, value: f32) {
if let Some(el) = self.get_mut(id) {
if let UiKind::ProgressBar {
value: ref mut v, ..
} = &mut el.kind
{
*v = value.clamp(0.0, 1.0);
}
}
}
pub fn set_text(&mut self, id: &str, content: &str) {
if let Some(el) = self.get_mut(id) {
if let UiKind::Text {
content: ref mut c, ..
} = &mut el.kind
{
*c = content.to_string();
}
}
}
pub fn compute_position(&self, element: &UiElement, screen_w: f32, screen_h: f32) -> [f32; 2] {
let (ax, ay) = match element.anchor {
Anchor::TopLeft => (0.0, 0.0),
Anchor::TopCenter => (screen_w / 2.0, 0.0),
Anchor::TopRight => (screen_w, 0.0),
Anchor::CenterLeft => (0.0, screen_h / 2.0),
Anchor::Center => (screen_w / 2.0, screen_h / 2.0),
Anchor::CenterRight => (screen_w, screen_h / 2.0),
Anchor::BottomLeft => (0.0, screen_h),
Anchor::BottomCenter => (screen_w / 2.0, screen_h),
Anchor::BottomRight => (screen_w, screen_h),
};
[ax + element.offset[0], ay + element.offset[1]]
}
pub fn handle_input(
&mut self,
mouse_x: f32,
mouse_y: f32,
mouse_pressed: bool,
screen_w: f32,
screen_h: f32,
) {
for element in &mut self.elements {
if !element.visible {
continue;
}
let pos = {
let (ax, ay) = match element.anchor {
Anchor::TopLeft => (0.0, 0.0),
Anchor::TopCenter => (screen_w / 2.0, 0.0),
Anchor::TopRight => (screen_w, 0.0),
Anchor::CenterLeft => (0.0, screen_h / 2.0),
Anchor::Center => (screen_w / 2.0, screen_h / 2.0),
Anchor::CenterRight => (screen_w, screen_h / 2.0),
Anchor::BottomLeft => (0.0, screen_h),
Anchor::BottomCenter => (screen_w / 2.0, screen_h),
Anchor::BottomRight => (screen_w, screen_h),
};
[ax + element.offset[0], ay + element.offset[1]]
};
let in_bounds = mouse_x >= pos[0]
&& mouse_x <= pos[0] + element.size[0]
&& mouse_y >= pos[1]
&& mouse_y <= pos[1] + element.size[1];
match &mut element.kind {
UiKind::Button {
pressed, hovered, ..
} => {
*hovered = in_bounds;
*pressed = in_bounds && mouse_pressed;
}
UiKind::Slider {
value, min, max, ..
} => {
if in_bounds && mouse_pressed && element.size[0] > 0.0 {
let local_x = mouse_x - pos[0];
let ratio = (local_x / element.size[0]).clamp(0.0, 1.0);
*value = *min + ratio * (*max - *min);
}
}
_ => {}
}
}
}
}