shadowengine2d 2.0.0

A comprehensive 2D game engine built in Rust with ECS, rendering, audio, assets, animations, and scene management
Documentation
#[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),
        }
    }
}
/*
 * MIT License
 * 
 * Copyright (c) 2025 ShadowEngine2D
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

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()
    }
}