bevy_pages 0.3.1

A lightweight and elegant framework to upgrade your Bevy UI experience.
Documentation
# Quick Start


**This tutorial shows how to get started with `bevy_pages` by creating a simple counter app.**

## Setting everything up


Add `bevy_pages` to your project via `cargo add bevy_pages` or simply add this line to your `Cargo.toml`:

```toml
[dependencies]
bevy_pages = "*" # Or whatever version you want to use
```

To actually make `bevy_pages` usable, you need to add the `PagesPlugin`:

```rust
use bevy_pages::PagesPlugin;

fn main() {
    // `let app = App::new();`

    // Setup your app here

    app.add_plugins(PagesPlugin::default());

    app.run();
}
```

## Creating the XML


It's recommended to start your XML page with:

```xml
<?xml version="1.0" encoding="utf-8"?>
<Page

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/DraftedDev/bevy_pages/master/schema.xsd">
    <!-- Your page content here -->
</Page>
```

If you get errors regarding the `schema.xsd` file, you can download it
from [here](https://raw.githubusercontent.com/DraftedDev/bevy_pages/master/schema.xsd) and add it to your project. Make
sure to make your XML point to the location of the `schema.xsd` file if you choose this way.

You can create UI nodes just like you would in HTML. For our counter app we will use following built-in elements:

- `<Text></Text>` for text display
- `<Button></Button>` for user interaction
- `<Node></Node>` for the layout

```xml
<?xml version="1.0" encoding="utf-8"?>
<Page

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/DraftedDev/bevy_pages/master/schema.xsd"

>
    <Node>
        <Text>
            Counter
        </Text>

        <Text>
            0
        </Text>

        <Node>
            <Button>
                <Text>-</Text>
            </Button>

            <Button>
                <Text

                        font-size="20px"

                        color="white"

                >
                    Reset
                </Text>
            </Button>

            <Button>
                <Text>+</Text>
            </Button>
        </Node>
    </Node>
</Page>
```

## Styling

Every node, including the `Page` node support common bevy styling via attributes. Different elements can also have
different attributes.

Also note the `id` attribute which uniquely identifies an element.

The resulting XML page becomes:

```xml
<?xml version="1.0" encoding="utf-8"?>
<Page

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        justify-content="center"

        align-items="center"

        xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/DraftedDev/bevy_pages/master/schema.xsd"

>
    <Node

            flex-direction="column"

            align-items="center"

            row-gap="24px"

            padding="32px"

    >
        <Text

                font-size="36px"

                color="white"

        >
            Counter
        </Text>

        <Text

                font-size="72px"

                color="#4CAF50"

                id="counter"

        >
            0
        </Text>

        <Node

                flex-direction="row"

                column-gap="16px"

                justify-content="center"

                align-items="center"

        >
            <Button

                    id="decrement"

                    width="64px"

                    height="64px"

                    justify-content="center"

                    align-items="center"

            >
                <Text

                        font-size="32px"

                        color="white"

                >
                    -
                </Text>
            </Button>

            <Button

                    id="reset"

                    width="120px"

                    height="64px"

                    justify-content="center"

                    align-items="center"

            >
                <Text

                        font-size="20px"

                        color="white"

                >
                    Reset
                </Text>
            </Button>

            <Button

                    id="increment"

                    width="64px"

                    height="64px"

                    justify-content="center"

                    align-items="center"

            >
                <Text

                        font-size="32px"

                        color="white"

                >+
                </Text>
            </Button>
        </Node>
    </Node>
</Page>
```

## Spawning the page

Now that our page is complete, we need to integrate it into our app.
Since a `Page` is just a loaded asset, we would need to create an asset-loaded callback to spawn the loaded page.
Fortunately, `PageSpawner` handles all of this:

```rust
fn main() {
    let app = App::new();
    // ...
    app.add_systems(Startup, setup);
    // ...
    app.run();
}

fn setup(mut commands: Commands, assets: Res<AssetServer>, mut spawner: ResMut<PageSpawner>) {
    /*
    Spawn necessary components for 2d rendering:
    
    commands.spawn((Camera2d::default(), Camera::default(), Transform::default()));
    */

    // Directly load the page asset
    let handle = assets.load("counter.xml");

    // Spawn the page
    spawner.spawn(handle);
}
```

## Adding Interactivity

Interactivity is managed via element events and observers.

You can manage the page itself via the `Page` resource.

This `on_click` function shows how to handle our button clicks:

```rust
fn main() {
    // ...
    app.add_observer(on_click);
    // ...
}

// This function is called whenever an element is clicked.
fn on_click(
    click: On<ElementClick>,
    page: Res<Page>,
    mut text_query: Query<&mut Properties<TextProps>>,
) {
    // Get the entity with the element ID "counter"
    let counter_entity = page.get("counter");
    // Get the "counter" entity from the text query
    let mut counter_text = text_query.get_mut(counter_entity).unwrap();
    // Parse the content of the counter entity into an integer
    let counter = counter_text.default.content.parse::<i32>().unwrap();

    // If button has ID "increment" => increment counter
    if click.matches_id("increment") {
        counter_text.mutate(|props| props.content = (counter + 1).to_string());
    }

    // If button has ID "decrement" => decrement counter
    if click.matches_id("decrement") {
        counter_text.mutate(|props| props.content = (counter - 1).to_string());
    }

    // If button has ID "reset" => reset counter
    if click.matches_id("reset") {
        counter_text.mutate(|props| props.content = "0".to_string());
    }
}
```

Notice that we use `mutate(|props| ...)` to mutate the properties of the text entity.
Every widget has its own `Properties<WidgetProps>`. The `Properties` struct contains the properties of the widget for
different states: `default`, `hover` and `click`.
Using `props.mutate(|props| ...)` mutates **all** the property states.

## Despawning the Page

If you switch to a new scene, you may want to despawn the page. You can use the `PageSpawner` to do so:

```rust
fn despawn_page(mut commands: Commands, mut spawner: ResMut<PageSpawner>) {
    spawner.despawn(&mut commands);
}
```

This will despawn the currently active page.

## Summary

You now have a basic counter app with increment, decrement and reset functionality.

You can now do `cargo run` to run your app and look at the beautiful page you created!