#[derive(Debug, Clone)]
pub struct SliderWidget {
pub rect: Rect,
pub min: f32,
pub max: f32,
pub value: f32,
pub step: f32,
pub style: SliderStyle,
pub dragging: bool,
}
#[derive(Debug, Clone)]
pub struct SliderStyle {
pub track_color: Color,
pub fill_color: Color,
pub knob_color: Color,
}
impl SliderStyle {
pub fn default() -> Self {
Self {
track_color: Color::new(0.3, 0.3, 0.3, 1.0),
fill_color: Color::new(0.2, 0.5, 0.8, 1.0),
knob_color: Color::WHITE,
}
}
}
#[derive(Debug, Clone)]
pub struct TextInputWidget {
pub rect: Rect,
pub text: String,
pub placeholder: String,
pub focused: bool,
pub style: TextInputStyle,
}
#[derive(Debug, Clone)]
pub struct TextInputStyle {
pub bg_color: Color,
pub border_color: Color,
pub text_color: Color,
pub placeholder_color: Color,
}
impl TextInputStyle {
pub fn default() -> Self {
Self {
bg_color: Color::new(1.0, 1.0, 1.0, 1.0),
border_color: Color::new(0.7, 0.7, 0.7, 1.0),
text_color: Color::BLACK,
placeholder_color: Color::new(0.6, 0.6, 0.6, 1.0),
}
}
}
use crate::{
math::{Position, Size, Color, Rect},
entity::{EntityId, EntityManager, Transform, Sprite},
};
#[derive(Debug)]
pub struct UiContext {
widgets: Vec<Widget>,
next_id: u32,
}
impl UiContext {
pub fn new() -> Self {
Self {
widgets: Vec::new(),
next_id: 1,
}
}
pub fn add_widget(&mut self, widget: Widget) -> WidgetId {
let id = WidgetId(self.next_id);
self.next_id += 1;
self.widgets.push(widget);
id
}
pub fn text(&mut self, content: impl Into<String>, position: Position, color: Color) -> WidgetId {
let widget = Widget::Text(TextWidget {
content: content.into(),
position,
color,
font_size: 16.0,
});
self.add_widget(widget)
}
pub fn button(&mut self, rect: Rect, text: impl Into<String>, style: ButtonStyle) -> WidgetId {
let widget = Widget::Button(ButtonWidget {
rect,
text: text.into(),
style,
hovered: false,
pressed: false,
});
self.add_widget(widget)
}
pub fn panel(&mut self, rect: Rect, color: Color) -> WidgetId {
let widget = Widget::Panel(PanelWidget { rect, color });
self.add_widget(widget)
}
pub fn slider(&mut self, rect: Rect, min: f32, max: f32, value: f32, step: f32, style: SliderStyle) -> WidgetId {
let widget = Widget::Slider(SliderWidget {
rect,
min,
max,
value,
step,
style,
dragging: false,
});
self.add_widget(widget)
}
pub fn text_input(&mut self, rect: Rect, placeholder: impl Into<String>, style: TextInputStyle) -> WidgetId {
let widget = Widget::TextInput(TextInputWidget {
rect,
text: String::new(),
placeholder: placeholder.into(),
focused: false,
style,
});
self.add_widget(widget)
}
pub fn render_to_entities(&self, entity_manager: &mut EntityManager) -> Vec<EntityId> {
let mut entity_ids = Vec::new();
for widget in &self.widgets {
match widget {
Widget::Panel(panel) => {
let entity = entity_manager.create_entity();
let transform = Transform::new(panel.rect.position());
let sprite = Sprite::new(panel.color, panel.rect.size());
entity_manager.add_transform(entity, transform);
entity_manager.add_sprite(entity, sprite);
entity_ids.push(entity);
}
Widget::Button(button) => {
let bg_entity = entity_manager.create_entity();
let bg_transform = Transform::new(button.rect.position());
let bg_color = if button.pressed {
button.style.pressed_color
} else if button.hovered {
button.style.hover_color
} else {
button.style.normal_color
};
let bg_sprite = Sprite::new(bg_color, button.rect.size());
entity_manager.add_transform(bg_entity, bg_transform);
entity_manager.add_sprite(bg_entity, bg_sprite);
entity_ids.push(bg_entity);
}
Widget::Text(_text) => {}
Widget::Slider(_) => {}
Widget::TextInput(_) => {}
}
}
entity_ids
}
pub fn clear(&mut self) {
self.widgets.clear();
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct WidgetId(u32);
#[derive(Debug, Clone)]
pub enum Widget {
Text(TextWidget),
Button(ButtonWidget),
Panel(PanelWidget),
Slider(SliderWidget),
TextInput(TextInputWidget),
}
#[derive(Debug, Clone)]
pub struct TextWidget {
pub content: String,
pub position: Position,
pub color: Color,
pub font_size: f32,
}
#[derive(Debug, Clone)]
pub struct ButtonWidget {
pub rect: Rect,
pub text: String,
pub style: ButtonStyle,
pub hovered: bool,
pub pressed: bool,
}
#[derive(Debug, Clone)]
pub struct ButtonStyle {
pub normal_color: Color,
pub hover_color: Color,
pub pressed_color: Color,
pub text_color: Color,
}
impl ButtonStyle {
pub fn default() -> Self {
Self {
normal_color: Color::new(0.3, 0.3, 0.3, 1.0),
hover_color: Color::new(0.4, 0.4, 0.4, 1.0),
pressed_color: Color::new(0.2, 0.2, 0.2, 1.0),
text_color: Color::WHITE,
}
}
pub fn primary() -> Self {
Self {
normal_color: Color::new(0.2, 0.4, 0.8, 1.0),
hover_color: Color::new(0.3, 0.5, 0.9, 1.0),
pressed_color: Color::new(0.1, 0.3, 0.7, 1.0),
text_color: Color::WHITE,
}
}
}
#[derive(Debug, Clone)]
pub struct PanelWidget {
pub rect: Rect,
pub color: Color,
}
#[derive(Debug)]
pub struct Layout {
pub rect: Rect,
pub padding: f32,
pub spacing: f32,
}
impl Layout {
pub fn new(rect: Rect) -> Self {
Self {
rect,
padding: 10.0,
spacing: 5.0,
}
}
pub fn with_padding(mut self, padding: f32) -> Self {
self.padding = padding;
self
}
pub fn with_spacing(mut self, spacing: f32) -> Self {
self.spacing = spacing;
self
}
pub fn vertical_layout(&self, item_count: usize, item_height: f32) -> Vec<Position> {
let mut positions = Vec::new();
let total_spacing = (item_count.saturating_sub(1)) as f32 * self.spacing;
let available_height = self.rect.height - 2.0 * self.padding - total_spacing;
let mut y = self.rect.y + self.padding;
for _ in 0..item_count {
positions.push(Position::new(self.rect.x + self.padding, y));
y += item_height + self.spacing;
}
positions
}
pub fn horizontal_layout(&self, item_count: usize, item_width: f32) -> Vec<Position> {
let mut positions = Vec::new();
let total_spacing = (item_count.saturating_sub(1)) as f32 * self.spacing;
let available_width = self.rect.width - 2.0 * self.padding - total_spacing;
let mut x = self.rect.x + self.padding;
for _ in 0..item_count {
positions.push(Position::new(x, self.rect.y + self.padding));
x += item_width + self.spacing;
}
positions
}
}
impl Default for UiContext {
fn default() -> Self {
Self::new()
}
}