gres/src/view/ui.rs
2026-08-04 03:33:19 +05:00

187 lines
5.6 KiB
Rust

use bevy::feathers::{FeathersPlugins, containers::*, controls::*, dark_theme::create_dark_theme, display::*, theme::UiTheme};
use bevy::ui_widgets::{Activate};
use bevy::prelude::*;
use crate::grid::{Grid, GridElement};
use crate::simulation::SimulationStatus;
use crate::view::editor::EditorTools;
pub struct UIPlugin;
impl Plugin for UIPlugin {
fn build(&self, app: &mut App) {
app.add_plugins(FeathersPlugins)
.insert_resource(UiTheme(create_dark_theme()))
.add_systems(Startup, ui.spawn())
.add_systems(Update, count_water_amount);
}
}
#[derive(Component, Clone, Default)]
struct WaterLabel;
#[derive(Component, Clone, Default)]
struct SpeedLabel;
#[derive(Component, Clone, Default)]
struct ToolLabel;
const MAX_FREQUENCY: u32 = 512;
const MIN_FREQUENCY: u32 = 1;
fn count_water_amount(grid: Res<Grid>, text: Single<&mut Text, With<WaterLabel>>) {
let mut sum: u32 = 0;
for i in 0..grid.cell_amount() {
let GridElement::Water { amount: water_amount } = grid.data[i as usize] else {
continue;
};
sum += water_amount;
}
let mut label = text.into_inner();
label.0 = format!("Water amount: {} liters",sum);
}
fn ui() -> impl Scene {
bsn! {
Node {
width: percent(100),
height: percent(100),
align_items: AlignItems::Start,
justify_content: JustifyContent::Start,
display: Display::Flex,
flex_direction: FlexDirection::Row,
column_gap: px(8)
}
Children [
debug_column()
editor_column()
]
}
}
fn debug_column() -> impl Scene {
bsn! {
Node {
display: Display::Flex,
flex_direction: FlexDirection::Column,
align_items: AlignItems::Stretch,
justify_content: JustifyContent::Start,
padding: px(8),
row_gap: px(8),
width: px(256),
min_width: px(256),
}
pane()
pane_header()
pane_body()
Children [
water_amount_label(),
pause_button(),
simulation_speed(),
]
}
}
fn editor_column() -> impl Scene {
bsn! {
Node {
display: Display::Flex,
flex_direction: FlexDirection::Column,
align_items: AlignItems::Stretch,
justify_content: JustifyContent::Start,
padding: px(8),
row_gap: px(8),
width: px(256),
min_width: px(256),
}
pane()
pane_header()
pane_body()
Children [
label("Tools")
ToolLabel,
@FeathersButton {
@caption: bsn!{label("None")}
}
on(|_activate: On<Activate>, mut tool: ResMut<EditorTools>, mut label: Single<&mut Text, With<ToolLabel>>| {
*tool = EditorTools::None;
label.0 = "Current tool: None".to_string();
}),
@FeathersButton {
@caption: bsn!{label("Paint")}
}
on(|_activate: On<Activate>, mut tool: ResMut<EditorTools>, mut label: Single<&mut Text, With<ToolLabel>>| {
*tool = EditorTools::Paint { position: UVec3::default(), block: GridElement::Solid };
label.0 = "Current tool: Paint".to_string();
}),
]
}
}
fn water_amount_label() -> impl Scene {
bsn! {
Node {
width: percent(100),
height: px(32),
}
Children [
label("")
WaterLabel
]
}
}
fn pause_button() -> impl Scene {
bsn!{
@FeathersButton {
@caption: bsn! { label("Toggle simulation") }
}
on(|_activate: On<Activate>, current_state: Res<State<SimulationStatus>>, mut next_state: ResMut<NextState<SimulationStatus>>| {
next_state.set(match current_state.get() {
SimulationStatus::Paused => SimulationStatus::Continued,
SimulationStatus::Continued => SimulationStatus::Paused,
});
})
}
}
fn simulation_speed() -> impl Scene {
bsn! {
Node {
display: Display::Flex,
align_items: AlignItems::Stretch,
justify_content: JustifyContent::Start,
width: percent(100),
height: px(32),
}
Children [
@FeathersButton {
@caption: bsn! { label("<<") }
}
on(|_activate: On<Activate>, mut time: ResMut<Time<Fixed>>, mut label: Single<&mut Text, With<SpeedLabel>>|{
let mut current_frequency = (1./time.timestep().as_secs_f64()) as u32;
current_frequency = if current_frequency <= MIN_FREQUENCY {MIN_FREQUENCY} else {current_frequency / 2};
time.set_timestep_hz(current_frequency as f64);
label.0 = format!("{} hz", current_frequency);
})
,
label("")
SpeedLabel
Node {
justify_self: JustifySelf::Stretch
},
@FeathersButton {
@caption: bsn! { label(">>") }
}
on(|_activate: On<Activate>, mut time: ResMut<Time<Fixed>>, mut label: Single<&mut Text, With<SpeedLabel>>|{
let mut current_frequency = (1./time.timestep().as_secs_f64()) as u32;
current_frequency = if current_frequency >= MAX_FREQUENCY {MAX_FREQUENCY} else {current_frequency * 2};
time.set_timestep_hz(current_frequency as f64);
label.0 = format!("{} hz", current_frequency);
}),
]
}
}