use bevy::prelude::*; use monologue_trees::debug::*; fn main() { App::new() .add_plugins(( DefaultPlugins.set(WindowPlugin { primary_window: Some(Window { title: "Audio Inspect".into(), resolution: (640., 480.).into(), ..default() }), ..default() }), DebugInfoPlugin, )) .add_systems(PreStartup, (load,)) .add_systems(Startup, (init,)) .add_systems(Update, (save, update, sort_buttons)) .run() } /// Stores audio handles so they don't get dropped #[derive(Component, Debug)] struct AudioItem(Handle); #[derive(Component)] struct Container; /// /// Load audio assets /// TODO: Does this load new items when they're added to the folder? fn load(server: Res) { // To prevent handles from being dropped, // they are stored on entities created triggered on AssetEvent::Created let _yeet = server.load_folder("audio").expect("Load audios"); } /// /// Initialize audio inspector UI fn init(mut commands: Commands) { commands.spawn(Camera3dBundle { ..default() }); commands.spawn(( NodeBundle { style: Style { width: Val::Percent(100.0), height: Val::Percent(100.0), align_items: AlignItems::FlexStart, align_content: AlignContent::FlexStart, flex_direction: FlexDirection::Row, flex_wrap: FlexWrap::Wrap, ..default() }, background_color: BackgroundColor(Color::MIDNIGHT_BLUE), ..default() }, Container, )); } /// /// Save loaded audio sources to entities fn save( mut audio_load_events: EventReader>, server: Res, mut commands: Commands, container_q: Query>, ) { for event in audio_load_events.iter() { match event { AssetEvent::Created { handle } => { let style = TextStyle { color: Color::BLACK, font_size: 16.0, ..default() }; let handle_path = server .get_handle_path(handle.clone()) .expect("Get handle path"); let path = handle_path.path().to_str().expect("Convert path to str"); let title = path .split("/") .last() .expect("Extracting filename") .trim_end_matches(".ogg"); commands .spawn(( Name::new(format!("{}", title)), ButtonBundle { style: Style { border: UiRect::all(Val::Px(1.0)), margin: UiRect::all(Val::Px(2.0)), padding: UiRect::all(Val::Px(2.0)), ..default() }, border_color: BorderColor(Color::BLACK), ..default() }, AudioBundle { source: handle.clone(), settings: PlaybackSettings { mode: bevy::audio::PlaybackMode::Loop, paused: true, ..default() }, }, )) .with_children(|parent| { parent.spawn(TextBundle::from_section(title, style)); }) .set_parent(container_q.single()); } AssetEvent::Modified { .. } => { debug_assert!(false, "Audio file was modified, not handled!") } AssetEvent::Removed { .. } => { debug_assert!(false, "Audio file was deleted, not handled!") } } } } fn sort_buttons( mut container: Query<&mut Children, (With, Changed)>, names: Query<&Name, With>, ) { container.iter_mut().for_each(|mut children| { children.sort_by(|&a, &b| { names .get(a) .expect("Button has name") .cmp(names.get(b).expect("Button has name")) }); }); } /// Update loop; play/pause/volume fn update( mut interactions: Query< (&Interaction, &AudioSink, &mut BackgroundColor), (With