use bevy::input_focus::InputFocus; use crate::client::colors; use bevy::prelude::*; use bevy::ui::FocusPolicy; use bevy::ui::widget::ImageNodeSize; use crate::shared::config::part::PartConfig; use crate::shared::config::recipe::RecipesConfig; use crate::shared::config::ship_editor::ShipEditorConfig; pub fn ui_plugin(app: &mut App) { app .insert_resource(ShipEditorConfigHolder::default()) .add_systems(Startup, (setup_ui, load_ship_editor_config)) .add_systems(PreUpdate, (fill_part_list, fill_parts)); } #[derive(Component)] pub struct PendingPartList; #[derive(Component)] pub struct PendingPart(pub Handle); #[derive(Component)] pub struct PartList; #[derive(Component)] pub struct PartEntry(pub PartConfig); #[derive(Resource, Default)] struct ShipEditorConfigHolder { handle: Option>, } fn load_ship_editor_config( mut config: ResMut, asset_server: Res, ) { config.handle = Some(asset_server.load("config/ship_editor.se.toml")); } fn setup_ui(mut commands: Commands) { commands.spawn(( Node { width: Val::Percent(100.0), height: Val::Percent(100.0), ..Default::default() }, children![ ( Node { position_type: PositionType::Absolute, margin: UiRect::AUTO.with_bottom(Val::Px(0.0)), width: Val::Percent(50.0), height: Val::Px(128.0), ..Default::default() }, BackgroundColor(colors::MANTLE), children![()] ), ( Node { position_type: PositionType::Absolute, margin: UiRect::AUTO.with_left(Val::Px(0.0)), width: Val::Px(200.0), height: Val::Percent(100.0), ..Default::default() }, BackgroundColor(colors::MANTLE), children![ ( Node { display: Display::Grid, width: Val::Percent(100.0), height: Val::Percent(100.0), grid_template_columns: RepeatedGridTrack::auto(2), grid_template_rows: RepeatedGridTrack::px(2, 100.0), justify_items: JustifyItems::Center, align_items: AlignItems::Center, ..Default::default() }, PendingPartList, ) ] ) ], )); } fn fill_part_list( pending_part_list: Query>, mut commands: Commands, ship_editor_config_holder: Res, ship_editor_config: Res>, asset_server: Res, ) { let Some(handle) = ship_editor_config_holder.handle.clone() else { return }; if let Some(strong_ship_editor_config) = ship_editor_config.get(&handle) { for pending_part_list in pending_part_list.iter() { let mut pending_part_list = commands.entity(pending_part_list); pending_part_list .insert(PartList) .remove::(); pending_part_list.with_children(|parent| { let mut part_list = Vec::with_capacity(strong_ship_editor_config.part_list.len()); for (i, (part_name, icon)) in strong_ship_editor_config.part_list.iter().enumerate() { part_list.push(( icon.order, (Node { width: Val::Px(50.0), height: Val::Px(50.0), margin: UiRect::axes(Val::Px(10.0), Val::Px(0.0)), ..Default::default() }, Button, PendingPart(asset_server.load(format!("config/parts/{}.part.toml", part_name.to_lowercase())))), )); } part_list.sort_by(|a, b| a.0.cmp(&b.0)); for (_, part) in part_list { parent.spawn(part); } }); } } } fn fill_parts( pending_parts: Query<(Entity, &PendingPart)>, mut commands: Commands, assets: Res>, asset_server: Res, ) { for (entity, pending_part) in pending_parts.iter() { if let Some(strong_part_config) = assets.get(&pending_part.0) { commands.entity(entity) .insert(PartEntry(strong_part_config.clone())) .insert(ImageNode::new(asset_server.load(strong_part_config.part.sprite_disconnected.clone()))) .remove::(); } } }