# 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">
</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!