From b2961310f531bdca97488e097aa49ecbe6c76f4f Mon Sep 17 00:00:00 2001 From: ghostly_zsh Date: Mon, 6 Apr 2026 22:05:39 -0500 Subject: [PATCH] feat: recipe list button-ified --- crates/unified/src/client/crafting/ui.rs | 70 ++++++++++++++++++------ 1 file changed, 54 insertions(+), 16 deletions(-) diff --git a/crates/unified/src/client/crafting/ui.rs b/crates/unified/src/client/crafting/ui.rs index af497c96f9f4f7ae71b8680054533b6df2535058..f7f447bead9242430e9808fae6c2124a199281c6 100644 --- a/crates/unified/src/client/crafting/ui.rs +++ b/crates/unified/src/client/crafting/ui.rs @@ -1,3 +1,5 @@ +use std::collections::HashMap; + use bevy::{input_focus::{AutoFocus, InputFocus}, ui::RelativeCursorPosition}; use crate::{attachment::PartInShip, client::colors, config::recipe::RecipesConfig, ecs::{CanCraft, CraftingUi, Drill, MainCamera, Me, Part, SingleStorage, ToggleDrillEvent}, prelude::*}; @@ -6,25 +8,29 @@ pub fn crafting_ui_plugin(app: &mut App) { app.init_resource::(); app.add_systems(Startup, load_recipes); app.add_systems(PreUpdate, (initial_create_recipe_list, update_recipe_list)); - app.add_systems(Update, (close_button, drill_button, drill_state_change, single_storage_display)); + app.add_systems(Update, (close_button, drill_button, drill_state_change, + single_storage_display, recipe_buttons)); } #[derive(Component)] struct CloseButton(Entity); // stores corresponding menu entity -#[derive(Component)] +#[derive(Component, Clone)] struct PreviousInteraction(Interaction); #[derive(Component)] struct DrillButton(Entity); // stores corresponding part #[derive(Component)] struct SingleStorageDisplay(Entity); // stores corresponding part #[derive(Component)] -struct RecipesHolder; +struct RecipesHolder(Entity); // stores corresponding part #[derive(Component)] -struct PendingRecipesHolder; +struct PendingRecipesHolder(Entity); // stores corresponding part #[derive(Resource, Default)] struct RecipeCollection { handle: Option>, } +// TODO: use recipe inputs for client-side validation? +#[derive(Component, Clone)] +struct RecipeElement(Entity, String, HashMap); // stores corresponding part and recipe's part name and inputs fn load_recipes(asset_server: Res, mut recipe_collection: ResMut) { recipe_collection.handle = Some(asset_server.load("config/recipes.rc.toml")); @@ -147,23 +153,23 @@ fn setup_ui( flex_direction: FlexDirection::Column, ..Default::default() }, - PendingRecipesHolder, + PendingRecipesHolder(parent_part), )); }); } fn initial_create_recipe_list( mut commands: Commands, - added_recipes_holders: Query>, + added_recipes_holders: Query<(Entity, &PendingRecipesHolder)>, recipe_collection: ResMut, recipes_config: Res>, ) { if let Some(strong_recipes_config) = recipes_config.get(&recipe_collection.handle.clone().unwrap()) { - for recipe_holder in &added_recipes_holders { + for (recipe_holder, pending_recipes_holder) in &added_recipes_holders { let mut recipe_holder = commands.get_entity(recipe_holder).unwrap(); - create_recipe_list(&mut recipe_holder, strong_recipes_config); + create_recipe_list(pending_recipes_holder.0, &mut recipe_holder, strong_recipes_config); recipe_holder - .insert(RecipesHolder) + .insert(RecipesHolder(pending_recipes_holder.0)) .remove::(); } } @@ -173,7 +179,7 @@ fn update_recipe_list( recipe_collection: ResMut, assets: ResMut>, mut commands: Commands, - recipes_holders: Query>, + recipes_holders: Query<(Entity, &RecipesHolder)>, ) { let Some(handle) = recipe_collection.handle.as_ref() else { return @@ -184,16 +190,17 @@ fn update_recipe_list( if *id == handle.id() { debug!("recipe list config modified - reloading lists"); let strong_recipes_config = assets.get(*id).unwrap(); - for recipe_holder in &recipes_holders { - let mut recipe_holder = commands.get_entity(recipe_holder).unwrap(); + for (recipe_holder_entity, recipes_holder) in &recipes_holders { + let mut recipe_holder = commands.get_entity(recipe_holder_entity).unwrap(); recipe_holder.despawn_children(); - create_recipe_list(&mut recipe_holder, strong_recipes_config); + create_recipe_list(recipes_holder.0, &mut recipe_holder, strong_recipes_config); } } } } } fn create_recipe_list( + parent_entity: Entity, recipe_holder: &mut EntityCommands, strong_recipes_config: &RecipesConfig, ) { @@ -205,6 +212,14 @@ fn create_recipe_list( .collect::>().join(", "); ui_recipes.push(( recipe.order, + (Node { + width: Val::Auto, + ..Default::default() + }, + RecipeElement(parent_entity, module_name.clone(), recipe.inputs.clone()), + BackgroundColor(colors::MANTLE), + PreviousInteraction(Interaction::None), + Button), (Node { ..Default::default() }, @@ -212,19 +227,42 @@ fn create_recipe_list( font_size: 10.0, ..Default::default() }, - Text::new(format!("{}: {}", module_name, resource_list))) + Text::new(format!("{}: {}", module_name, resource_list))), )); } } // ordering stuff ui_recipes.sort_by(|a, b| a.0.cmp(&b.0)); - let ui_recipes = ui_recipes.iter().map(|recipe| &recipe.1).collect::>(); recipe_holder.with_children(move |parent| { for recipe in ui_recipes { - parent.spawn(recipe.clone()); + parent.spawn(recipe.1.clone()) + .with_child(recipe.2); } }); } +fn recipe_buttons( + mut interaction_query: Query<(&Interaction, &mut PreviousInteraction, &mut BackgroundColor, &RecipeElement), + Changed>, +) { + for (interaction, mut previous_interaction, mut color, recipe) in &mut interaction_query { + match *interaction { + Interaction::Pressed => { + *color = colors::SURFACE_1.into(); + } + Interaction::Hovered => { + *color = colors::SURFACE_0.into(); + if previous_interaction.0 == Interaction::Pressed { + // released + debug!("{:?} {} {:?}", recipe.0, recipe.1, recipe.2); + } + } + Interaction::None => { + *color = colors::MANTLE.into(); + } + } + previous_interaction.0 = *interaction; + } +} fn drill_button( mut interaction_query: Query<