lumecs 0.0.1

Experimental GUI backed by Bevy ECS + usual suspects
pub use lumecs::prelude::*;

#[derive(Debug, Clone)]
enum Message {
    HeaderClick,
    ItemClick(usize),
    ScrollDown(Point),
    A,
    B,
    ShiftC,
}

struct App {
    header_text: String,
    bottom_text: String,
    scroll_position: Point,
}

fn bottom(bottom_text: &str) -> impl Widget<'_, Message> {
    Container::flex_row().child(Text::new(bottom_text))
}

fn list(app: &App) -> impl Widget<'_, Message> {
    Container::flex_column()
        .flex_grow(1.0)
        .gap(1.0)
        .overflow_y_hidden()
        .scroll_y(app.scroll_position)
        .on_scroll(Message::ScrollDown)
        .children(
            (0..100)
                .map(|i| {
                    Container::flex()
                        .background_color("#00ffff")
                        .on_click(Message::ItemClick(i))
                        .child(Text::new(format!("Item {i}")).color("#111111"))
                })
                .collect(),
        )
}

impl Runner<'_> for App {
    type M = Message;

    fn update(&mut self, message: Message) {
        match message {
            Message::HeaderClick => {
                self.header_text = "Header clicked!".to_string();
            }
            Message::ItemClick(index) => {
                self.bottom_text = format!("Item {index} clicked!");
            }
            Message::ScrollDown(delta) => {
                self.scroll_position += delta;
            }
            Message::A => {
                self.bottom_text = "Key A pressed!".to_string();
            }
            Message::B => {
                self.bottom_text = "Key B pressed!".to_string();
            }
            Message::ShiftC => {
                self.bottom_text = "Shift + C pressed!".to_string();
            }
        }
    }

    fn view(&self) -> impl Widget<'_, Self::M> {
        Root::flex_column()
            .on_key_press(KeyCode::KeyA, Message::A)
            .on_key_press(KeyCode::KeyB, Message::B)
            .on_key_press(SHIFT + KeyCode::KeyC, Message::ShiftC)
            .background_color("#212121")
            .align_items_stretch()
            .child(
                Container::flex()
                    .background_color("#00ff00")
                    .height(60.0)
                    .on_click(Message::HeaderClick)
                    .child(Container::flex().flex_basis(450.0))
                    .child(
                        Container::flex()
                            .flex_grow(1.0)
                            .child(Text::new(self.header_text.clone()).color("#ff0000")),
                    ),
            )
            .child(
                Container::grid()
                    .columns(|t| t.fr(1.0).fr(1.0))
                    .rows(|t| t.fr(1.0).fr(1.0))
                    .child(Text::new("Grid one"))
                    .child(Text::new("Grid two"))
                    .child(Text::new("Grid three"))
                    .child(Text::new("Grid four")),
            )
            .child(
                Container::grid()
                    .background_color("#3d3d3d")
                    .columns(|c| c.fr(1.0).fr(1.0))
                    .gap(5.0)
                    .children(vec![
                        Container::flex()
                            .background_color("#ff0")
                            .child(Text::new("Child one")),
                        Container::flex()
                            .background_color("#f00")
                            .child(Text::new("Child two")),
                    ]),
            )
            .child(Container::flex_row().child(Text::new("Flyover")))
            .child(
                Container::flex_row()
                    .flex_grow(1.0)
                    .gap(5.0)
                    .child(
                        Container::flex_column()
                            .flex_grow(1.0)
                            .child(Text::new("One").font_size(50.0).line_height(60.0)),
                    )
                    .child(
                        Container::flex_column()
                            .background_color("#ff00ff")
                            .flex_grow(1.0)
                            .gap(5.0)
                            .child(Text::new("Two two two two two"))
                            .child(Text::new("Three")),
                    )
                    .child(
                        Container::flex_column()
                            .background_color("#0000ff")
                            .flex_grow(1.0)
                            .gap(5.0)
                            .child(Text::new("Two"))
                            .child(Text::new("Three")),
                    ),
            )
            .child(list(&self))
            .child(bottom(&self.bottom_text))
    }
}

fn main() {
    tracing_subscriber::fmt::init();

    let app = App {
        header_text: "One".to_string(),
        bottom_text: "Bottom".to_string(),
        scroll_position: Point::default(),
    };

    run(app).expect("Failed to run the application");
}