use bevy::prelude::*;
use beuvy::{
DeclarativeActionMessage, DeclarativeActionSpec, DeclarativeUiBuildContext,
DeclarativeUiPlugin, DeclarativeUiRuntimeValues, FontResource, UiKitPlugin, UiValue,
parse_declarative_ui_asset, set_action_resolver, spawn_declarative_ui_tree_collect_slots,
};
const PAGE_SOURCE: &str = include_str!("declarative_vue_page.vue");
fn main() {
set_action_resolver(|name| match name {
"setStatus" => Some(DeclarativeActionSpec {
action_id: "demo.set_status",
param_names: vec!["status"],
}),
_ => None,
});
App::new()
.insert_resource(DeclarativeUiRuntimeValues::default())
.add_plugins(DefaultPlugins.set(WindowPlugin {
primary_window: Some(Window {
title: "beuvy declarative vue example".to_string(),
resolution: (1280, 860).into(),
..default()
}),
..default()
}))
.add_plugins(UiKitPlugin)
.add_plugins(DeclarativeUiPlugin::default())
.add_systems(Startup, setup)
.add_systems(Startup, seed_runtime_values)
.add_systems(Update, handle_actions)
.run();
}
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
commands.spawn(Camera2d);
commands.insert_resource(FontResource::from_handle(
asset_server.load("fonts/SarasaFixedSC-Regular.ttf"),
));
let asset = parse_declarative_ui_asset(PAGE_SOURCE).expect("page should parse");
let root_context = DeclarativeUiBuildContext::default().with_root(UiValue::object([
("title", UiValue::from("Beuvy declarative example")),
("subtitle", UiValue::from("Vue-style template, Rust props, and action dispatch")),
("items", UiValue::list([
UiValue::object([
("name", UiValue::from("Engine")),
("count", UiValue::from(3_i32)),
]),
UiValue::object([
("name", UiValue::from("Shield")),
("count", UiValue::from(8_i32)),
]),
UiValue::object([
("name", UiValue::from("Fuel")),
("count", UiValue::from(42_i32)),
]),
])),
]));
commands
.spawn((
Node {
width: Val::Percent(100.0),
height: Val::Percent(100.0),
padding: UiRect::all(Val::Px(32.0)),
justify_content: JustifyContent::Center,
align_items: AlignItems::Center,
..default()
},
BackgroundColor(Color::srgb_u8(241, 245, 249)),
))
.with_children(|parent| {
parent
.spawn((
Node {
width: Val::Px(980.0),
max_width: Val::Percent(100.0),
padding: UiRect::all(Val::Px(24.0)),
border_radius: BorderRadius::all(Val::Px(18.0)),
..default()
},
BorderColor::all(Color::srgb_u8(203, 213, 225)),
BackgroundColor(Color::WHITE),
))
.with_children(|panel| {
spawn_declarative_ui_tree_collect_slots(panel, &asset, root_context);
});
});
}
fn seed_runtime_values(mut values: ResMut<DeclarativeUiRuntimeValues>) {
values.set("ui.search", "");
values.set("ui.status", "idle");
}
fn handle_actions(
mut actions: MessageReader<DeclarativeActionMessage>,
mut values: ResMut<DeclarativeUiRuntimeValues>,
) {
for action in actions.read() {
if action.action_id != "demo.set_status" {
continue;
}
if let Some(status) = action.params.get("status") {
values.set("ui.status", status.clone());
}
}
}