x11-overlay 0.1.0

A library for creating overlay interfaces on X11 systems using Cairo for rendering
Documentation
#[cfg(test)]
mod tests {
    use super::*;
    use crate::graphics::{Rectangle};

    // Mock component for testing
    struct MockComponent {
        bounds: Rectangle,
        updated: bool,
    }

    impl MockComponent {
        fn new(x: i16, y: i16, width: u16, height: u16) -> Self {
            Self {
                bounds: Rectangle::new(x, y, width, height),
                updated: false,
            }
        }
    }

    impl Component for MockComponent {
        fn render(&self, _graphics: &mut crate::graphics::GraphicsContext) -> anyhow::Result<()> {
            Ok(())
        }

        fn bounds(&self) -> Rectangle {
            self.bounds
        }

        fn update(&mut self, _delta_time: f64) -> bool {
            self.updated = !self.updated;
            self.updated
        }
    }

    #[test]
    fn test_spacing_creation() {
        let spacing = Spacing::new(10, 20, 30, 40);
        assert_eq!(spacing.top, 10);
        assert_eq!(spacing.right, 20);
        assert_eq!(spacing.bottom, 30);
        assert_eq!(spacing.left, 40);

        let uniform = Spacing::uniform(15);
        assert_eq!(uniform.top, 15);
        assert_eq!(uniform.right, 15);
        assert_eq!(uniform.bottom, 15);
        assert_eq!(uniform.left, 15);
    }

    #[test]
    fn test_layout_item_creation() {
        let component = MockComponent::new(10, 20, 100, 50);
        let item = LayoutItem::new(Box::new(component));

        assert_eq!(item.computed_bounds.x, 10);
        assert_eq!(item.computed_bounds.y, 20);
        assert_eq!(item.computed_bounds.width, 100);
        assert_eq!(item.computed_bounds.height, 50);
        assert_eq!(item.visible, true);
    }

    #[test]
    fn test_layout_container_creation() {
        let bounds = Rectangle::new(0, 0, 800, 600);
        let container = LayoutContainer::new(LayoutType::HorizontalFlow, bounds);

        assert_eq!(container.bounds().x, 0);
        assert_eq!(container.bounds().y, 0);
        assert_eq!(container.bounds().width, 800);
        assert_eq!(container.bounds().height, 600);
    }

    #[test]
    fn test_layout_builder() {
        let bounds = Rectangle::new(0, 0, 400, 300);

        let horizontal = LayoutBuilder::horizontal(bounds);
        assert!(matches!(horizontal.layout_type, LayoutType::HorizontalFlow));

        let vertical = LayoutBuilder::vertical(bounds);
        assert!(matches!(vertical.layout_type, LayoutType::VerticalFlow));

        let grid = LayoutBuilder::grid(bounds, 3);
        assert!(matches!(grid.layout_type, LayoutType::Grid { columns: 3 }));
    }

    #[test]
    fn test_horizontal_layout() {
        let bounds = Rectangle::new(0, 0, 400, 100);
        let mut container = LayoutBuilder::horizontal(bounds)
            .with_gap(10)
            .with_alignment(Alignment::Start);

        // Add two components
        let comp1 = MockComponent::new(0, 0, 50, 30);
        let comp2 = MockComponent::new(0, 0, 80, 40);

        container.add_component(Box::new(comp1));
        container.add_component(Box::new(comp2));

        // Layout should position components horizontally
        container.layout().unwrap();

        // First component should be at the start
        assert_eq!(container.items[0].computed_bounds.x, 0);
        assert_eq!(container.items[0].computed_bounds.y, 0);

        // Second component should be positioned after first + gap
        assert_eq!(container.items[1].computed_bounds.x, 50 + 10); // width of first + gap
        assert_eq!(container.items[1].computed_bounds.y, 0);
    }

    #[test]
    fn test_vertical_layout() {
        let bounds = Rectangle::new(0, 0, 100, 400);
        let mut container = LayoutBuilder::vertical(bounds)
            .with_gap(5)
            .with_alignment(Alignment::Start);

        // Add two components
        let comp1 = MockComponent::new(0, 0, 50, 30);
        let comp2 = MockComponent::new(0, 0, 60, 40);

        container.add_component(Box::new(comp1));
        container.add_component(Box::new(comp2));

        // Layout should position components vertically
        container.layout().unwrap();

        // First component should be at the start
        assert_eq!(container.items[0].computed_bounds.x, 0);
        assert_eq!(container.items[0].computed_bounds.y, 0);

        // Second component should be positioned after first + gap
        assert_eq!(container.items[1].computed_bounds.x, 0);
        assert_eq!(container.items[1].computed_bounds.y, 30 + 5); // height of first + gap
    }

    #[test]
    fn test_grid_layout() {
        let bounds = Rectangle::new(0, 0, 200, 200);
        let mut container = LayoutBuilder::grid(bounds, 2); // 2 columns

        // Add four components
        for i in 0..4 {
            let comp = MockComponent::new(0, 0, 40, 30);
            container.add_component(Box::new(comp));
        }

        container.layout().unwrap();

        // Cell dimensions should be 100x100 (200/2 columns, 200/2 rows)
        let cell_width = 200 / 2;
        let cell_height = 200 / 2;

        // Check positioning of first component (top-left)
        assert_eq!(container.items[0].computed_bounds.x, 0);
        assert_eq!(container.items[0].computed_bounds.y, 0);

        // Check positioning of second component (top-right)
        assert_eq!(container.items[1].computed_bounds.x, cell_width as i16);
        assert_eq!(container.items[1].computed_bounds.y, 0);

        // Check positioning of third component (bottom-left)
        assert_eq!(container.items[2].computed_bounds.x, 0);
        assert_eq!(container.items[2].computed_bounds.y, cell_height as i16);

        // Check positioning of fourth component (bottom-right)
        assert_eq!(container.items[3].computed_bounds.x, cell_width as i16);
        assert_eq!(container.items[3].computed_bounds.y, cell_height as i16);
    }

    #[test]
    fn test_alignment_center() {
        let bounds = Rectangle::new(0, 0, 200, 100);
        let mut container = LayoutBuilder::horizontal(bounds)
            .with_alignment(Alignment::Center);

        // Add a component smaller than the container
        let comp = MockComponent::new(0, 0, 50, 30);
        container.add_component(Box::new(comp));

        container.layout().unwrap();

        // Component should be centered vertically (horizontal layout centers in cross-axis)
        let expected_y = (100 - 30) / 2; // (container_height - component_height) / 2
        assert_eq!(container.items[0].computed_bounds.y, expected_y as i16);
    }

    #[test]
    fn test_padding() {
        let bounds = Rectangle::new(0, 0, 200, 100);
        let padding = Spacing::uniform(10);
        let mut container = LayoutBuilder::horizontal(bounds)
            .with_padding(padding);

        let comp = MockComponent::new(0, 0, 50, 30);
        container.add_component(Box::new(comp));

        container.layout().unwrap();

        // Component should be positioned considering padding
        assert_eq!(container.items[0].computed_bounds.x, 10); // left padding
        assert_eq!(container.items[0].computed_bounds.y, 10); // top padding
    }

    #[test]
    fn test_container_visibility() {
        let bounds = Rectangle::new(0, 0, 100, 100);
        let mut container = LayoutContainer::new(LayoutType::Fixed, bounds);

        // Empty container should not be visible
        assert!(!container.is_visible());

        // Add a component
        let comp = MockComponent::new(0, 0, 50, 30);
        container.add_component(Box::new(comp));

        // Now container should be visible
        assert!(container.is_visible());
    }

    #[test]
    fn test_container_update() {
        let bounds = Rectangle::new(0, 0, 100, 100);
        let mut container = LayoutContainer::new(LayoutType::Fixed, bounds);

        // Add a component
        let comp = MockComponent::new(0, 0, 50, 30);
        container.add_component(Box::new(comp));

        // Update should return true if any component needs redraw
        let needs_redraw = container.update(0.016);
        assert!(needs_redraw); // MockComponent alternates its update return value
    }
}